Skip to content
GitLab
Menu
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
May 11, 2022
by
gentlespoon
Committed by
Melledy
May 12, 2022
Browse files
Slowly recover HP near statue
parent
8c71af26
Changes
3
Show whitespace changes
Inline
Side-by-side
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,134 +47,146 @@ 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
);
public
long
getLastUsed
()
{
return
player
.
getSpringLastUsed
();
}
if
(!
isAlive
)
{
entity
.
getWorld
().
broadcastPacket
(
new
PacketAvatarLifeStateChangeNotify
(
entity
.
getAvatar
()));
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
scheduleAutoRecover
(
GameSession
session
)
{
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
;
public
AutoRecoverTimerTick
(
GameSession
session
)
{
this
.
session
=
session
;
}
public
void
run
()
{
autoRecover
(
session
);
cancelAutoRecover
();
// 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
void
refillSpringVolume
()
{
// 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
);
// 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
);
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
()
{
refillSpringVolume
();
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
;
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
));
}
}
}
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
);
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
);
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
;
}
floa
t
targetHP
=
maxHP
*
a
utoRecoverPercentage
/
100
;
in
t
targetHP
=
maxHP
*
getA
utoRecover
y
Percentage
()
/
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
)
{
int
needHP
=
targetHP
-
currentHP
;
int
currentVolume
=
getCurrentVolume
();
if
(
currentVolume
>=
needHP
)
{
// sufficient
sotsSVBalanc
e
-
=
need
SV
;
setCurrentVolume
(
currentVolum
e
-
need
HP
)
;
}
else
{
// insufficient balance
need
SV
=
sotsSVBalanc
e
;
sotsSVBalance
=
0
;
need
HP
=
currentVolum
e
;
setCurrentVolume
(
0
)
;
}
player
.
setProperty
(
PlayerProperty
.
PROP_CUR_SPRING_VOLUME
,
sotsSVBalance
);
player
.
setSpringLastUsed
(
System
.
currentTimeMillis
()
/
1000
);
float
newHP
=
currentHP
+
needSV
/
100
;
// convert SV to HP
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
));
updateAvatarCurHP
(
session
,
entity
,
newHP
);
}
});
}
}
p
rivate
void
updateAvatarCurHP
(
GameSession
session
,
EntityAvatar
entity
,
float
newHP
)
{
// T
ODO: 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
)
);
se
ssion
.
send
(
new
PacketEntityFightPropUpdateNotify
(
entity
,
FightProperty
.
FIGHT_PROP_CUR_HP
)
);
p
ublic
void
refillSpringVolume
(
)
{
// T
emporary: 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
setMaxVolume
(
8500000
);
// Temporary: Auto enable 100% statue recovery until we can adjust statue settings in game
// TODO: remove
setAutoRecoveryPercentage
(
100
);
se
tIsAutoRecoveryEnabled
(
true
);
Avatar
avatar
=
entity
.
getAvatar
();
avatar
.
setCurrentHp
(
newHP
);
session
.
send
(
new
PacketAvatarFightPropUpdateNotify
(
avatar
,
FightProperty
.
FIGHT_PROP_CUR_HP
));
player
.
save
();
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
);
}
}
}
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
();
}
}
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
();
}
}
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