SotSManager.java 7.54 KB
Newer Older
1
package emu.grasscutter.game.managers;
2
3
4

import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.avatar.Avatar;
5
import emu.grasscutter.game.entity.EntityAvatar;
6
7
8
9
10
11
12
13
14
15
16
17
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass;
import emu.grasscutter.net.proto.PropChangeReasonOuterClass;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;

import java.util.List;
18
19
import java.util.Timer;
import java.util.TimerTask;
20
21
22
23

// Statue of the Seven Manager
public class SotSManager {

24
25
    // NOTE: Spring volume balance *1  = fight prop HP *100

26
    private final Player player;
27
    private Timer autoRecoverTimer;
28

29
30
    public final static int GlobalMaximumSpringVolume = 8500000;

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
    public SotSManager(Player player) {
        this.player = player;
    }

    public boolean getIsAutoRecoveryEnabled() {
        return player.getProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE) == 1;
    }

    public void setIsAutoRecoveryEnabled(boolean enabled) {
        player.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, enabled ? 1 : 0);
    }

    public int getAutoRecoveryPercentage() {
        return player.getProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT);
    }

    public void setAutoRecoveryPercentage(int percentage) {
        player.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, percentage);
    }

    // autoRevive automatically revives all team members.
    public void autoRevive(GameSession session) {
        player.getTeamManager().getActiveTeam().forEach(entity -> {
            boolean isAlive = entity.isAlive();
55
56
57
58
59
60
61
            float currentHP = entity.getAvatar().getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
            float maxHP = entity.getAvatar().getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
//            Grasscutter.getLogger().debug("" + entity.getAvatar().getAvatarData().getName() + "\t" + currentHP + "/" + maxHP + "\t" + (isAlive ? "ALIVE":"DEAD"));
            float newHP = (float)(maxHP * 0.3);
            if (currentHP < newHP) {
                updateAvatarCurHP(session, entity, newHP);
            }
62
63
64
65
66
67
68
            if (!isAlive) {
                entity.getWorld().broadcastPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar()));
            }
        });
    }

    public void scheduleAutoRecover(GameSession session) {
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
        if (autoRecoverTimer == null) {
            autoRecoverTimer = new Timer();
            autoRecoverTimer.schedule(new AutoRecoverTimerTick(session), 2500);
        }
    }

    public void cancelAutoRecover() {
        if (autoRecoverTimer != null) {
            autoRecoverTimer.cancel();
            autoRecoverTimer = null;
        }
    }

    private class AutoRecoverTimerTick extends TimerTask
    {
        private GameSession session;

        public AutoRecoverTimerTick(GameSession session) {
            this.session = session;
        }
        public void run() {
            autoRecover(session);
            cancelAutoRecover();
        }
93
94
    }

95
96
97
    public void refillSpringVolume() {
        // TODO: max spring volume depends on level of the statues in Mondstadt and Liyue.
        // https://genshin-impact.fandom.com/wiki/Statue_of_The_Seven#:~:text=region%20of%20Inazuma.-,Statue%20Levels,-Upon%20first%20unlocking
98
99
        player.setProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME, 8500000);

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
        long now = System.currentTimeMillis() / 1000;
        long secondsSinceLastUsed = now - player.getSpringLastUsed();
        float percentageRefilled = (float)secondsSinceLastUsed / 15 / 100; // 15s = 1% max volume
        int maxVolume = player.getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME);
        int currentVolume = player.getProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME);
        if (currentVolume < maxVolume) {
            int volumeRefilled = (int)(percentageRefilled * maxVolume);
            int newVolume = currentVolume + volumeRefilled;
            if (currentVolume + volumeRefilled > maxVolume) {
                newVolume = maxVolume;
            }
            player.setProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME, newVolume);
        }
        player.setSpringLastUsed(now);
        player.save();
    }

    // autoRecover checks player setting to see if auto recover is enabled, and refill HP to the predefined level.
    public void autoRecover(GameSession session) {
        // TODO: In MP, respect SotS settings from the HOST.
120
121
        boolean  isAutoRecoveryEnabled = getIsAutoRecoveryEnabled();
        int autoRecoverPercentage = getAutoRecoveryPercentage();
122
        Grasscutter.getLogger().debug("isAutoRecoveryEnabled: " + isAutoRecoveryEnabled + "\tautoRecoverPercentage: " + autoRecoverPercentage);
123
124
125
126
127
128
129
130
131
132
133
134

        if (isAutoRecoveryEnabled) {
            player.getTeamManager().getActiveTeam().forEach(entity -> {
                float maxHP = entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
                float currentHP = entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
                if (currentHP == maxHP) {
                    return;
                }
                float targetHP = maxHP * autoRecoverPercentage / 100;

                if (targetHP > currentHP) {
                    float needHP = targetHP - currentHP;
135
136
137
138
139
140
141
142
143
144
145
146
147
                    float needSV = needHP * 100; // convert HP needed to Spring Volume needed

                    int sotsSVBalance = player.getProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME);
                    if (sotsSVBalance >= needSV) {
                        // sufficient
                        sotsSVBalance -= needSV;
                    } else {
                        // insufficient balance
                        needSV = sotsSVBalance;
                        sotsSVBalance = 0;
                    }
                    player.setProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME, sotsSVBalance);
                    player.setSpringLastUsed(System.currentTimeMillis() / 1000);
148

149
                    float newHP = currentHP + needSV / 100; // convert SV to HP
150

151
                    updateAvatarCurHP(session, entity, newHP);
152
153
154
155
156
                }
            });
        }
    }

157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
    private void updateAvatarCurHP(GameSession session, EntityAvatar entity, float newHP) {
        // TODO: Figure out why client shows current HP instead of added HP.
        //    Say an avatar had 12000 and now has 14000, it should show "2000".
        //    The client always show "+14000" which is incorrect.
        entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
        session.send(new PacketEntityFightPropChangeReasonNotify(entity, FightProperty.FIGHT_PROP_CUR_HP,
                newHP, List.of(3), PropChangeReasonOuterClass.PropChangeReason.PROP_CHANGE_STATUE_RECOVER,
                ChangeHpReasonOuterClass.ChangeHpReason.ChangeHpAddStatue));
        session.send(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));

        Avatar avatar = entity.getAvatar();
        avatar.setCurrentHp(newHP);
        session.send(new PacketAvatarFightPropUpdateNotify(avatar, FightProperty.FIGHT_PROP_CUR_HP));
        player.save();
    }

173
174

}