mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-12-15 00:15:33 +01:00
Improved satiation (#2055)
* Natural satiation decreasing Graphic showing satiation when eating (usually) * Reworking values to match original * Little fixes * Satiation bar matches real values Revival correctly updates bar * Greatly simplify handling Some fixes * Inline variables Add TODO for bug * Satiation works correctly Finally it all works as intended * Remove unnecessary packets * Improve satiation reduction handling
This commit is contained in:
@@ -84,8 +84,8 @@ public class Avatar {
|
||||
@Getter @Setter private int level = 1;
|
||||
@Getter @Setter private int exp;
|
||||
@Getter @Setter private int promoteLevel;
|
||||
@Getter @Setter private int satiation; // ?
|
||||
@Getter @Setter private int satiationPenalty; // ?
|
||||
@Getter @Setter private int satiation; // Fullness
|
||||
@Getter @Setter private int satiationPenalty; // When eating too much
|
||||
@Getter @Setter private float currentHp;
|
||||
private float currentEnergy;
|
||||
|
||||
@@ -204,12 +204,30 @@ public class Avatar {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean addSatiation(float value) {
|
||||
if (this.satiation >= 100) return false;
|
||||
public boolean addSatiation(int value) {
|
||||
if (this.satiation >= 10000) return false;
|
||||
this.satiation += value;
|
||||
return true;
|
||||
}
|
||||
|
||||
public float reduceSatiation(int value) {
|
||||
if (this.satiation == 0) return 0;
|
||||
this.satiation -= value;
|
||||
if(this.satiation < 0) {
|
||||
this.satiation = 0;
|
||||
}
|
||||
return this.satiation;
|
||||
}
|
||||
|
||||
public float reduceSatiationPenalty(int value) {
|
||||
if (this.satiationPenalty == 0) return 0;
|
||||
this.satiationPenalty -= value;
|
||||
if(this.satiationPenalty < 0) {
|
||||
this.satiationPenalty = 0;
|
||||
}
|
||||
return this.satiationPenalty;
|
||||
}
|
||||
|
||||
public GameItem getEquipBySlot(EquipType slot) {
|
||||
return this.getEquips().get(slot.getValue());
|
||||
}
|
||||
@@ -879,8 +897,8 @@ public class Avatar {
|
||||
avatarInfo.putPropMap(PlayerProperty.PROP_LEVEL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, this.getLevel()));
|
||||
avatarInfo.putPropMap(PlayerProperty.PROP_EXP.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_EXP, this.getExp()));
|
||||
avatarInfo.putPropMap(PlayerProperty.PROP_BREAK_LEVEL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_BREAK_LEVEL, this.getPromoteLevel()));
|
||||
avatarInfo.putPropMap(PlayerProperty.PROP_SATIATION_VAL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_SATIATION_VAL, 0));
|
||||
avatarInfo.putPropMap(PlayerProperty.PROP_SATIATION_PENALTY_TIME.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_SATIATION_PENALTY_TIME, 0));
|
||||
avatarInfo.putPropMap(PlayerProperty.PROP_SATIATION_VAL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_SATIATION_VAL, this.getSatiation()));
|
||||
avatarInfo.putPropMap(PlayerProperty.PROP_SATIATION_PENALTY_TIME.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_SATIATION_PENALTY_TIME, this.getSatiationPenalty()));
|
||||
|
||||
return avatarInfo.build();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package emu.grasscutter.game.managers;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.player.BasePlayerManager;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.PlayerProperty;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarSatiationDataNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketPlayerGameTimeNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketPlayerTimeNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarPropNotify;
|
||||
|
||||
public class SatiationManager extends BasePlayerManager {
|
||||
|
||||
public SatiationManager(Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
/********************
|
||||
* Change satiation
|
||||
********************/
|
||||
public synchronized boolean addSatiation(Avatar avatar, float satiationIncrease, int itemId) {
|
||||
|
||||
// Satiation is max 10000 but can go over in the case of overeating
|
||||
Map<Integer, Long> propMap = new HashMap<>();
|
||||
int satiation = Math.round(satiationIncrease * 100);
|
||||
float totalSatiation = ((satiationIncrease * 100) + avatar.getSatiation());
|
||||
|
||||
// Update client time
|
||||
updateTime();
|
||||
|
||||
// Calculate times
|
||||
var playerTime = (player.getClientTime() / 1000);
|
||||
float finishTime = playerTime + (totalSatiation / 30);
|
||||
|
||||
// Penalty
|
||||
long penaltyTime = playerTime;
|
||||
long penaltyValue = avatar.getSatiationPenalty();
|
||||
if(totalSatiation + avatar.getSatiation() > 10000 && penaltyValue == 0) {
|
||||
// Penalty is always 30sec
|
||||
penaltyTime += 30;
|
||||
penaltyValue = 3000;
|
||||
}
|
||||
|
||||
// Add satiation
|
||||
if (!addSatiationDirectly(avatar, satiation)) return false;
|
||||
propMap.put(PlayerProperty.PROP_SATIATION_VAL.getId(), Long.valueOf(satiation));
|
||||
propMap.put(PlayerProperty.PROP_SATIATION_PENALTY_TIME.getId(), penaltyValue);
|
||||
|
||||
// Send packets
|
||||
player.getSession().send(new PacketAvatarPropNotify(avatar, propMap));
|
||||
player.getSession().send(new PacketAvatarSatiationDataNotify(avatar, finishTime, penaltyTime));
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized boolean addSatiationDirectly(Avatar avatar, int value) {
|
||||
if (!avatar.addSatiation(value))
|
||||
return false;
|
||||
// Update avatar
|
||||
avatar.save();
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized void removeSatiationDirectly(Avatar avatar, int value) {
|
||||
avatar.reduceSatiation(value);
|
||||
avatar.reduceSatiationPenalty(3000);
|
||||
avatar.save();
|
||||
// Update avatar to no satiation
|
||||
updateSingleAvatar(avatar, 0);
|
||||
}
|
||||
|
||||
public synchronized void reduceSatiation() {
|
||||
/* Satiation may not reduce while paused on official but it will here */
|
||||
// Get all avatars with satiation
|
||||
player.getAvatars().forEach(avatar -> {
|
||||
// Ensure avatar isn't stuck in penalty
|
||||
if (avatar.getSatiationPenalty() > 0 && avatar.getSatiation() == 0) {
|
||||
avatar.reduceSatiationPenalty(3000);
|
||||
}
|
||||
|
||||
// Reduce satiation
|
||||
if (avatar.getSatiation() > 0) {
|
||||
// Reduce penalty first
|
||||
if (avatar.getSatiationPenalty() > 0) {
|
||||
// Penalty reduction rate is 1/s
|
||||
avatar.reduceSatiationPenalty(100);
|
||||
} else {
|
||||
// Satiation reduction rate is 0.3/s
|
||||
avatar.reduceSatiation(30);
|
||||
|
||||
// Update all packets every tick else it won't work
|
||||
// Surely there is a better way to handle this
|
||||
addSatiation(avatar, 0, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/********************
|
||||
* Player Updates
|
||||
********************/
|
||||
public synchronized void updateSingleAvatar(Avatar avatar, float givenTime) {
|
||||
float time = (player.getClientTime() / 1000) + givenTime;
|
||||
player.getSession().send(new PacketAvatarPropNotify(avatar));
|
||||
player.getSession().send(new PacketAvatarSatiationDataNotify(time, avatar));
|
||||
}
|
||||
|
||||
private void updateTime() {
|
||||
player.getSession().send(new PacketPlayerGameTimeNotify(player));
|
||||
player.getSession().send(new PacketPlayerTimeNotify(player));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import emu.grasscutter.game.managers.cooking.CookingCompoundManager;
|
||||
import emu.grasscutter.game.managers.cooking.CookingManager;
|
||||
import emu.grasscutter.game.managers.FurnitureManager;
|
||||
import emu.grasscutter.game.managers.ResinManager;
|
||||
import emu.grasscutter.game.managers.SatiationManager;
|
||||
import emu.grasscutter.game.managers.deforestation.DeforestationManager;
|
||||
import emu.grasscutter.game.managers.energy.EnergyManager;
|
||||
import emu.grasscutter.game.managers.forging.ActiveForgeData;
|
||||
@@ -164,6 +165,7 @@ public class Player {
|
||||
@Getter private transient ActivityManager activityManager;
|
||||
@Getter private transient PlayerBuffManager buffManager;
|
||||
@Getter private transient PlayerProgressManager progressManager;
|
||||
@Getter private transient SatiationManager satiationManager;
|
||||
|
||||
// Manager data (Save-able to the database)
|
||||
private PlayerProfile playerProfile; // Getter has null-check
|
||||
@@ -268,6 +270,7 @@ public class Player {
|
||||
this.furnitureManager = new FurnitureManager(this);
|
||||
this.cookingManager = new CookingManager(this);
|
||||
this.cookingCompoundManager=new CookingCompoundManager(this);
|
||||
this.satiationManager = new SatiationManager(this);
|
||||
}
|
||||
|
||||
// On player creation
|
||||
@@ -303,6 +306,7 @@ public class Player {
|
||||
this.furnitureManager = new FurnitureManager(this);
|
||||
this.cookingManager = new CookingManager(this);
|
||||
this.cookingCompoundManager=new CookingCompoundManager(this);
|
||||
this.satiationManager = new SatiationManager(this);
|
||||
}
|
||||
|
||||
public int getUid() {
|
||||
@@ -1100,6 +1104,9 @@ public class Player {
|
||||
|
||||
// Recharge resin.
|
||||
this.getResinManager().rechargeResin();
|
||||
|
||||
// Satiation
|
||||
this.getSatiationManager().reduceSatiation();
|
||||
}
|
||||
|
||||
private synchronized void doDailyReset() {
|
||||
|
||||
@@ -26,6 +26,7 @@ import emu.grasscutter.server.packet.send.PacketAddBackupAvatarTeamRsp;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarDieAnimationEndRsp;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarSatiationDataNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarTeamAllDataNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarTeamUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketChangeAvatarRsp;
|
||||
@@ -569,6 +570,8 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
}
|
||||
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 1f);
|
||||
// Satiation is reset when reviving an avatar
|
||||
player.getSatiationManager().removeSatiationDirectly(entity.getAvatar(), 15000);
|
||||
this.getPlayer().sendPacket(new PacketAvatarFightPropUpdateNotify(entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP));
|
||||
this.getPlayer().sendPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar()));
|
||||
return true;
|
||||
@@ -618,6 +621,7 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
FightProperty.FIGHT_PROP_CUR_HP,
|
||||
entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * .4f
|
||||
);
|
||||
player.getSatiationManager().removeSatiationDirectly(entity.getAvatar(), 15000);
|
||||
this.getPlayer().sendPacket(new PacketAvatarFightPropUpdateNotify(entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP));
|
||||
this.getPlayer().sendPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar()));
|
||||
}
|
||||
|
||||
@@ -785,7 +785,7 @@ public class InventorySystem extends BaseGameSystem {
|
||||
if (event.isCanceled()) return false;
|
||||
|
||||
float satiationIncrease = satiationParams[0] + ((float)satiationParams[1])/params.targetAvatar.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||
if (!params.targetAvatar.addSatiation(satiationIncrease)) { // Make sure avatar can eat
|
||||
if (!params.player.getSatiationManager().addSatiation(params.targetAvatar, satiationIncrease, itemData.getId())) { // Make sure avatar can eat
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,13 @@ public class PacketOpcodesUtils {
|
||||
PacketOpcodes.WorldPlayerRTTNotify,
|
||||
PacketOpcodes.UnionCmdNotify,
|
||||
PacketOpcodes.QueryPathReq,
|
||||
PacketOpcodes.QueryPathRsp
|
||||
PacketOpcodes.QueryPathRsp,
|
||||
|
||||
// Satiation sends these every tick
|
||||
PacketOpcodes.PlayerTimeNotify,
|
||||
PacketOpcodes.PlayerGameTimeNotify,
|
||||
PacketOpcodes.AvatarPropNotify,
|
||||
PacketOpcodes.AvatarSatiationDataNotify
|
||||
);
|
||||
|
||||
static {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package emu.grasscutter.server.packet.send;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.props.PlayerProperty;
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
@@ -9,19 +11,19 @@ import emu.grasscutter.net.proto.AvatarPropNotifyOuterClass.AvatarPropNotify;
|
||||
public class PacketAvatarPropNotify extends BasePacket {
|
||||
public PacketAvatarPropNotify(Avatar avatar) {
|
||||
super(PacketOpcodes.AvatarPropNotify);
|
||||
|
||||
|
||||
AvatarPropNotify proto = AvatarPropNotify.newBuilder()
|
||||
.setAvatarGuid(avatar.getGuid())
|
||||
.putPropMap(PlayerProperty.PROP_LEVEL.getId(), avatar.getLevel())
|
||||
.putPropMap(PlayerProperty.PROP_EXP.getId(), avatar.getExp())
|
||||
.putPropMap(PlayerProperty.PROP_BREAK_LEVEL.getId(), avatar.getPromoteLevel())
|
||||
.putPropMap(PlayerProperty.PROP_SATIATION_VAL.getId(), 0)
|
||||
.putPropMap(PlayerProperty.PROP_SATIATION_PENALTY_TIME.getId(), 0)
|
||||
.putPropMap(PlayerProperty.PROP_SATIATION_VAL.getId(), avatar.getSatiation())
|
||||
.putPropMap(PlayerProperty.PROP_SATIATION_PENALTY_TIME.getId(), avatar.getSatiationPenalty())
|
||||
.build();
|
||||
|
||||
|
||||
this.setData(proto);
|
||||
}
|
||||
|
||||
|
||||
public PacketAvatarPropNotify(Avatar avatar, PlayerProperty prop, int value) {
|
||||
super(PacketOpcodes.AvatarPropNotify);
|
||||
|
||||
@@ -29,7 +31,18 @@ public class PacketAvatarPropNotify extends BasePacket {
|
||||
.setAvatarGuid(avatar.getGuid())
|
||||
.putPropMap(prop.getId(), value)
|
||||
.build();
|
||||
|
||||
|
||||
this.setData(proto);
|
||||
}
|
||||
|
||||
public PacketAvatarPropNotify(Avatar avatar, Map<Integer, Long> propMap) {
|
||||
super(PacketOpcodes.AvatarPropNotify);
|
||||
|
||||
AvatarPropNotify proto = AvatarPropNotify.newBuilder()
|
||||
.setAvatarGuid(avatar.getGuid())
|
||||
.putAllPropMap(propMap)
|
||||
.build();
|
||||
|
||||
this.setData(proto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package emu.grasscutter.server.packet.send;
|
||||
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.net.proto.AvatarSatiationDataNotifyOuterClass.AvatarSatiationDataNotify;
|
||||
import emu.grasscutter.net.proto.AvatarSatiationDataOuterClass.AvatarSatiationData;
|
||||
|
||||
public class PacketAvatarSatiationDataNotify extends BasePacket {
|
||||
|
||||
public PacketAvatarSatiationDataNotify(Avatar avatar, float finishTime, long penaltyTime) {
|
||||
super(PacketOpcodes.AvatarSatiationDataNotify);
|
||||
|
||||
AvatarSatiationData.Builder avatarSatiation = AvatarSatiationData.newBuilder()
|
||||
.setAvatarGuid(avatar.getGuid())
|
||||
.setFinishTime(finishTime);
|
||||
|
||||
// Penalty for overeating
|
||||
if (penaltyTime > 0) {
|
||||
avatarSatiation.setPenaltyFinishTime(penaltyTime);
|
||||
}
|
||||
|
||||
avatarSatiation.build();
|
||||
|
||||
AvatarSatiationDataNotify notify = AvatarSatiationDataNotify.newBuilder()
|
||||
.addSatiationDataList(0, avatarSatiation)
|
||||
.build();
|
||||
|
||||
this.setData(notify);
|
||||
}
|
||||
|
||||
public PacketAvatarSatiationDataNotify(float time, Avatar avatar) {
|
||||
super(PacketOpcodes.AvatarSatiationDataNotify);
|
||||
|
||||
var avatarSatiation = AvatarSatiationData.newBuilder()
|
||||
.setAvatarGuid(avatar.getGuid())
|
||||
.setFinishTime(time + (avatar.getSatiation() / 30f))
|
||||
// Penalty time always ends before finish time
|
||||
.setPenaltyFinishTime(time + (avatar.getSatiationPenalty() / 100f))
|
||||
.build();
|
||||
|
||||
AvatarSatiationDataNotify notify = AvatarSatiationDataNotify.newBuilder()
|
||||
.addSatiationDataList(0, avatarSatiation)
|
||||
.build();
|
||||
|
||||
this.setData(notify);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user