Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
ziqian zhang
Grasscutter
Commits
65fcae79
Commit
65fcae79
authored
2 years ago
by
gentlespoon
Committed by
Melledy
2 years ago
Browse files
Options
Download
Email Patches
Plain Diff
Slowly recover HP near statue
parent
8c71af26
development
LintRatchet
stable
v1.4.4
v1.4.3
v1.4.2
v1.4.1
v1.4.0
v1.3.1
v1.3.0
v1.2.2-dev
No related merge requests found
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
src/main/java/emu/grasscutter/game/managers/SotSManager.java
+119
-106
src/main/java/emu/grasscutter/game/managers/SotSManager.java
src/main/java/emu/grasscutter/server/packet/recv/HandlerEnterTransPointRegionNotify.java
+1
-7
...erver/packet/recv/HandlerEnterTransPointRegionNotify.java
src/main/java/emu/grasscutter/server/packet/recv/HandlerExitTransPointRegionNotify.java
+2
-3
...server/packet/recv/HandlerExitTransPointRegionNotify.java
with
122 additions
and
116 deletions
+122
-116
src/main/java/emu/grasscutter/game/managers/SotSManager.java
View file @
65fcae79
package
emu.grasscutter.game.managers
;
import
ch.qos.logback.classic.Logger
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.game.avatar.Avatar
;
import
emu.grasscutter.game.entity.EntityAvatar
;
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.net.proto.ChangeHpReasonOuterClass
.ChangeHpReason
;
import
emu.grasscutter.net.proto.PropChangeReasonOuterClass
.PropChangeReason
;
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
;
...
...
@@ -24,7 +22,9 @@ public class SotSManager {
// NOTE: Spring volume balance *1 = fight prop HP *100
private
final
Player
player
;
private
final
Logger
logger
=
Grasscutter
.
getLogger
();
private
Timer
autoRecoverTimer
;
private
final
boolean
enablePriorityHealing
=
false
;
public
final
static
int
GlobalMaximumSpringVolume
=
8500000
;
...
...
@@ -38,6 +38,7 @@ public class SotSManager {
public
void
setIsAutoRecoveryEnabled
(
boolean
enabled
)
{
player
.
setProperty
(
PlayerProperty
.
PROP_IS_SPRING_AUTO_USE
,
enabled
?
1
:
0
);
player
.
save
();
}
public
int
getAutoRecoveryPercentage
()
{
...
...
@@ -46,49 +47,122 @@ public class SotSManager {
public
void
setAutoRecoveryPercentage
(
int
percentage
)
{
player
.
setProperty
(
PlayerProperty
.
PROP_SPRING_AUTO_USE_PERCENT
,
percentage
);
player
.
save
();
}
// autoRevive automatically revives all team members.
public
void
autoRevive
(
GameSession
session
)
{
player
.
getTeamManager
().
getActiveTeam
().
forEach
(
entity
->
{
boolean
isAlive
=
entity
.
isAlive
();
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
);
}
if
(!
isAlive
)
{
entity
.
getWorld
().
broadcastPacket
(
new
PacketAvatarLifeStateChangeNotify
(
entity
.
getAvatar
()));
}
});
public
long
getLastUsed
()
{
return
player
.
getSpringLastUsed
();
}
public
void
scheduleAutoRecover
(
GameSession
session
)
{
public
void
setLastUsed
()
{
player
.
setSpringLastUsed
(
System
.
currentTimeMillis
()
/
1000
);
player
.
save
();
}
public
int
getMaxVolume
()
{
return
player
.
getProperty
(
PlayerProperty
.
PROP_MAX_SPRING_VOLUME
);
}
public
void
setMaxVolume
(
int
volume
)
{
player
.
setProperty
(
PlayerProperty
.
PROP_MAX_SPRING_VOLUME
,
volume
);
player
.
save
();
}
public
int
getCurrentVolume
()
{
return
player
.
getProperty
(
PlayerProperty
.
PROP_CUR_SPRING_VOLUME
);
}
public
void
setCurrentVolume
(
int
volume
)
{
player
.
setProperty
(
PlayerProperty
.
PROP_CUR_SPRING_VOLUME
,
volume
);
setLastUsed
();
player
.
save
();
}
public
void
handleEnterTransPointRegionNotify
()
{
logger
.
trace
(
"Player entered statue region"
);
autoRevive
();
if
(
autoRecoverTimer
==
null
)
{
autoRecoverTimer
=
new
Timer
();
autoRecoverTimer
.
schedule
(
new
AutoRecoverTimerTick
(
session
),
25
00
);
autoRecoverTimer
.
schedule
(
new
AutoRecoverTimerTick
(
),
2500
,
150
00
);
}
}
public
void
cancelAutoRecover
()
{
public
void
handleExitTransPointRegionNotify
()
{
logger
.
trace
(
"Player left statue region"
);
if
(
autoRecoverTimer
!=
null
)
{
autoRecoverTimer
.
cancel
();
autoRecoverTimer
=
null
;
}
}
private
class
AutoRecoverTimerTick
extends
TimerTask
{
private
GameSession
session
;
// autoRevive automatically revives all team members.
public
void
autoRevive
()
{
player
.
getTeamManager
().
getActiveTeam
().
forEach
(
entity
->
{
boolean
isAlive
=
entity
.
isAlive
();
if
(
isAlive
)
{
return
;
}
logger
.
trace
(
"Reviving avatar "
+
entity
.
getAvatar
().
getAvatarData
().
getName
());
player
.
getTeamManager
().
reviveAvatar
(
entity
.
getAvatar
());
player
.
getTeamManager
().
healAvatar
(
entity
.
getAvatar
(),
30
,
0
);
});
}
public
AutoRecoverTimerTick
(
GameSession
session
)
{
this
.
session
=
session
;
}
private
class
AutoRecoverTimerTick
extends
TimerTask
{
// autoRecover checks player setting to see if auto recover is enabled, and refill HP to the predefined level.
public
void
run
()
{
autoRecover
(
session
);
cancelAutoRecover
();
refillSpringVolume
();
logger
.
trace
(
"isAutoRecoveryEnabled: "
+
getIsAutoRecoveryEnabled
()
+
"\tautoRecoverPercentage: "
+
getAutoRecoveryPercentage
());
if
(
getIsAutoRecoveryEnabled
())
{
List
<
EntityAvatar
>
activeTeam
=
player
.
getTeamManager
().
getActiveTeam
();
// When the statue does not have enough remaining volume:
// Enhanced experience: Enable priority healing
// The current active character will get healed first, then sequential.
// Vanilla experience: Disable priority healing
// Sequential healing based on character index.
int
priorityIndex
=
enablePriorityHealing
?
player
.
getTeamManager
().
getCurrentCharacterIndex
()
:
-
1
;
if
(
priorityIndex
>=
0
)
{
checkAndHealAvatar
(
activeTeam
.
get
(
priorityIndex
));
}
for
(
int
i
=
0
;
i
<
activeTeam
.
size
();
i
++)
{
if
(
i
!=
priorityIndex
)
{
checkAndHealAvatar
(
activeTeam
.
get
(
i
));
}
}
}
}
}
public
void
checkAndHealAvatar
(
EntityAvatar
entity
)
{
int
maxHP
=
(
int
)
(
entity
.
getFightProperty
(
FightProperty
.
FIGHT_PROP_MAX_HP
)
*
100
);
int
currentHP
=
(
int
)
(
entity
.
getFightProperty
(
FightProperty
.
FIGHT_PROP_CUR_HP
)
*
100
);
if
(
currentHP
==
maxHP
)
{
return
;
}
int
targetHP
=
maxHP
*
getAutoRecoveryPercentage
()
/
100
;
if
(
targetHP
>
currentHP
)
{
int
needHP
=
targetHP
-
currentHP
;
int
currentVolume
=
getCurrentVolume
();
if
(
currentVolume
>=
needHP
)
{
// sufficient
setCurrentVolume
(
currentVolume
-
needHP
);
}
else
{
// insufficient balance
needHP
=
currentVolume
;
setCurrentVolume
(
0
);
}
if
(
needHP
>
0
)
{
logger
.
trace
(
"Healing avatar "
+
entity
.
getAvatar
().
getAvatarData
().
getName
()
+
" +"
+
needHP
);
player
.
getTeamManager
().
healAvatar
(
entity
.
getAvatar
(),
0
,
needHP
);
player
.
getSession
().
send
(
new
PacketEntityFightPropChangeReasonNotify
(
entity
,
FightProperty
.
FIGHT_PROP_CUR_HP
,
((
float
)
needHP
/
100
),
List
.
of
(
3
),
PropChangeReason
.
PROP_CHANGE_STATUE_RECOVER
,
ChangeHpReason
.
ChangeHpAddStatue
));
player
.
getSession
().
send
(
new
PacketEntityFightPropUpdateNotify
(
entity
,
FightProperty
.
FIGHT_PROP_CUR_HP
));
}
}
}
...
...
@@ -96,84 +170,23 @@ public class SotSManager {
// Temporary: Max spring volume depends on level of the statues in Mondstadt and Liyue. Override until we have statue level.
// TODO: remove
// https://genshin-impact.fandom.com/wiki/Statue_of_The_Seven#:~:text=region%20of%20Inazuma.-,Statue%20Levels,-Upon%20first%20unlocking
player
.
setProperty
(
PlayerProperty
.
PROP_MAX_SPRING_VOLUME
,
8500000
);
setMaxVolume
(
8500000
);
// Temporary: Auto enable 100% statue recovery until we can adjust statue settings in game
// TODO: remove
player
.
setProperty
(
PlayerProperty
.
PROP_SPRING_AUTO_USE_PERCENT
,
100
);
player
.
setProperty
(
PlayerProperty
.
PROP_IS_SPRING_AUTO_USE
,
1
);
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.
boolean
isAutoRecoveryEnabled
=
getIsAutoRecoveryEnabled
();
int
autoRecoverPercentage
=
getAutoRecoveryPercentage
();
Grasscutter
.
getLogger
().
debug
(
"isAutoRecoveryEnabled: "
+
isAutoRecoveryEnabled
+
"\tautoRecoverPercentage: "
+
autoRecoverPercentage
);
setAutoRecoveryPercentage
(
100
);
setIsAutoRecoveryEnabled
(
true
);
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
;
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
);
float
newHP
=
currentHP
+
needSV
/
100
;
// convert SV to HP
updateAvatarCurHP
(
session
,
entity
,
newHP
);
}
});
int
maxVolume
=
getMaxVolume
();
int
currentVolume
=
getCurrentVolume
();
if
(
currentVolume
<
maxVolume
)
{
long
now
=
System
.
currentTimeMillis
()
/
1000
;
int
secondsSinceLastUsed
=
(
int
)
(
now
-
getLastUsed
());
// 15s = 1% max volume
int
volumeRefilled
=
secondsSinceLastUsed
*
maxVolume
/
15
/
100
;
logger
.
trace
(
"Statue has refilled HP volume: "
+
volumeRefilled
);
currentVolume
=
Math
.
min
(
currentVolume
+
volumeRefilled
,
maxVolume
);
logger
.
trace
(
"Statue remaining HP volume: "
+
currentVolume
);
setCurrentVolume
(
currentVolume
);
}
}
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
();
}
}
This diff is collapsed.
Click to expand it.
src/main/java/emu/grasscutter/server/packet/recv/HandlerEnterTransPointRegionNotify.java
View file @
65fcae79
...
...
@@ -11,12 +11,6 @@ import emu.grasscutter.server.game.GameSession;
public
class
HandlerEnterTransPointRegionNotify
extends
PacketHandler
{
@Override
public
void
handle
(
GameSession
session
,
byte
[]
header
,
byte
[]
payload
)
throws
Exception
{
Player
player
=
session
.
getPlayer
();
SotSManager
sotsManager
=
player
.
getSotSManager
();
sotsManager
.
refillSpringVolume
();
sotsManager
.
autoRevive
(
session
);
sotsManager
.
scheduleAutoRecover
(
session
);
// TODO: allow interaction with the SotS?
session
.
getPlayer
().
getSotSManager
().
handleEnterTransPointRegionNotify
();
}
}
This diff is collapsed.
Click to expand it.
src/main/java/emu/grasscutter/server/packet/recv/HandlerExitTransPointRegionNotify.java
View file @
65fcae79
package
emu.grasscutter.server.packet.recv
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.game.managers.SotSManager
;
import
emu.grasscutter.game.player.Player
;
import
emu.grasscutter.net.packet.Opcodes
;
...
...
@@ -11,8 +12,6 @@ import emu.grasscutter.server.game.GameSession;
public
class
HandlerExitTransPointRegionNotify
extends
PacketHandler
{
@Override
public
void
handle
(
GameSession
session
,
byte
[]
header
,
byte
[]
payload
)
throws
Exception
{
Player
player
=
session
.
getPlayer
();
SotSManager
sotsManager
=
player
.
getSotSManager
();
sotsManager
.
cancelAutoRecover
();
session
.
getPlayer
().
getSotSManager
().
handleExitTransPointRegionNotify
();
}
}
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment
Menu
Projects
Groups
Snippets
Help