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:
Thoronium
2023-02-15 17:32:59 -07:00
committed by GitHub
parent b4b8f1ec38
commit 1b2210f5a7
10 changed files with 1772 additions and 14 deletions

View File

@@ -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));
}
}