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
a8293102
Commit
a8293102
authored
Jun 07, 2022
by
Melledy
Committed by
GitHub
Jun 07, 2022
Browse files
Merge branch 'development' into stable
parents
304b9cb8
ecf7a81a
Changes
410
Show whitespace changes
Inline
Side-by-side
src/main/java/emu/grasscutter/game/managers/StaminaManager/README.md
0 → 100644
View file @
a8293102
# Stamina Manager
---
## UpdateStamina
```
java
// will use consumption.consumptionType as reason
public
int
updateStaminaRelative
(
GameSession
session
,
Consumption
consumption
);
```
```
java
public
int
updateStaminaAbsolute
(
GameSession
session
,
String
reason
,
int
newStamina
)
```
---
## Pause and Resume
```
java
public
void
startSustainedStaminaHandler
()
```
```
java
public
void
stopSustainedStaminaHandler
()
```
---
## Stamina change listeners and intercepting
### BeforeUpdateStaminaListener
```
java
import
emu.grasscutter.game.managers.StaminaManager.BeforeUpdateStaminaListener
;
// Listener sample: plugin disable CLIMB_JUMP stamina cost.
private
class
MyClass
implements
BeforeUpdateStaminaListener
{
// Make your class implement the listener, and pass in your class as a listener.
public
MyClass
()
{
getStaminaManager
().
registerBeforeUpdateStaminaListener
(
"myClass"
,
this
);
}
@Override
public
boolean
onBeforeUpdateStamina
(
String
reason
,
int
newStamina
)
{
// do not intercept this update
return
false
;
}
@Override
public
boolean
onBeforeUpdateStamina
(
String
reason
,
Consumption
consumption
)
{
// Try to intercept if this update is CLIMB_JUMP
if
(
consumption
.
consumptionType
==
ConsumptionType
.
CLIMB_JUMP
)
{
return
true
;
}
// If it is not CLIMB_JUMP, do not intercept.
return
false
;
}
}
```
### AfterUpdateStaminaListener
```
java
import
emu.grasscutter.game.managers.StaminaManager.AfterUpdateStaminaListener
;
// Listener sample: plugin listens for changes already made.
private
class
MyClass
implements
AfterUpdateStaminaListener
{
// Make your class implement the listener, and pass in your class as a listener.
public
MyClass
()
{
registerAfterUpdateStaminaListener
(
"myClass"
,
this
);
}
@Override
public
void
onAfterUpdateStamina
(
String
reason
,
int
newStamina
)
{
// ...
}
}
```
\ No newline at end of file
src/main/java/emu/grasscutter/game/managers/StaminaManager/StaminaManager.java
0 → 100644
View file @
a8293102
package
emu.grasscutter.game.managers.StaminaManager
;
import
ch.qos.logback.classic.Logger
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.command.commands.NoStaminaCommand
;
import
emu.grasscutter.data.GameData
;
import
emu.grasscutter.game.avatar.Avatar
;
import
emu.grasscutter.game.entity.EntityAvatar
;
import
emu.grasscutter.game.entity.GameEntity
;
import
emu.grasscutter.game.player.Player
;
import
emu.grasscutter.game.props.FightProperty
;
import
emu.grasscutter.game.props.LifeState
;
import
emu.grasscutter.game.props.PlayerProperty
;
import
emu.grasscutter.game.props.WeaponType
;
import
emu.grasscutter.net.proto.EntityMoveInfoOuterClass.EntityMoveInfo
;
import
emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo
;
import
emu.grasscutter.net.proto.MotionStateOuterClass.MotionState
;
import
emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType
;
import
emu.grasscutter.net.proto.VectorOuterClass.Vector
;
import
emu.grasscutter.net.proto.VehicleInteractTypeOuterClass.VehicleInteractType
;
import
emu.grasscutter.server.game.GameSession
;
import
emu.grasscutter.server.packet.send.*
;
import
emu.grasscutter.utils.Position
;
import
org.jetbrains.annotations.NotNull
;
import
java.util.*
;
import
static
emu
.
grasscutter
.
Configuration
.
GAME_OPTIONS
;
public
class
StaminaManager
{
// TODO: Skiff state detection?
private
final
Player
player
;
private
static
final
HashMap
<
String
,
HashSet
<
MotionState
>>
MotionStatesCategorized
=
new
HashMap
<>()
{{
put
(
"CLIMB"
,
new
HashSet
<>(
List
.
of
(
MotionState
.
MOTION_STATE_CLIMB
,
// sustained, when not moving no cost no recover
MotionState
.
MOTION_STATE_STANDBY_TO_CLIMB
// NOT OBSERVED, see MOTION_JUMP_UP_WALL_FOR_STANDBY
)));
put
(
"DASH"
,
new
HashSet
<>(
List
.
of
(
MotionState
.
MOTION_STATE_DANGER_DASH
,
// sustained
MotionState
.
MOTION_STATE_DASH
// sustained
)));
put
(
"FLY"
,
new
HashSet
<>(
List
.
of
(
MotionState
.
MOTION_STATE_FLY
,
// sustained
MotionState
.
MOTION_STATE_FLY_FAST
,
// sustained
MotionState
.
MOTION_STATE_FLY_SLOW
,
// sustained
MotionState
.
MOTION_STATE_POWERED_FLY
// sustained, recover
)));
put
(
"RUN"
,
new
HashSet
<>(
List
.
of
(
MotionState
.
MOTION_STATE_DANGER_RUN
,
// sustained, recover
MotionState
.
MOTION_STATE_RUN
// sustained, recover
)));
put
(
"SKIFF"
,
new
HashSet
<>(
List
.
of
(
MotionState
.
MOTION_STATE_SKIFF_BOARDING
,
// NOT OBSERVED even when boarding
MotionState
.
MOTION_STATE_SKIFF_DASH
,
// sustained, observed with waverider entity ID.
MotionState
.
MOTION_STATE_SKIFF_NORMAL
,
// sustained, OBSERVED when both normal and dashing
MotionState
.
MOTION_STATE_SKIFF_POWERED_DASH
// sustained, recover
)));
put
(
"STANDBY"
,
new
HashSet
<>(
List
.
of
(
MotionState
.
MOTION_STATE_DANGER_STANDBY_MOVE
,
// sustained, recover
MotionState
.
MOTION_STATE_DANGER_STANDBY
,
// sustained, recover
MotionState
.
MOTION_STATE_LADDER_TO_STANDBY
,
// NOT OBSERVED
MotionState
.
MOTION_STATE_STANDBY_MOVE
,
// sustained, recover
MotionState
.
MOTION_STATE_STANDBY
// sustained, recover
)));
put
(
"SWIM"
,
new
HashSet
<>(
List
.
of
(
MotionState
.
MOTION_STATE_SWIM_IDLE
,
// sustained
MotionState
.
MOTION_STATE_SWIM_DASH
,
// immediate and sustained
MotionState
.
MOTION_STATE_SWIM_JUMP
,
// NOT OBSERVED
MotionState
.
MOTION_STATE_SWIM_MOVE
// sustained
)));
put
(
"WALK"
,
new
HashSet
<>(
List
.
of
(
MotionState
.
MOTION_STATE_DANGER_WALK
,
// sustained, recover
MotionState
.
MOTION_STATE_WALK
// sustained, recover
)));
put
(
"OTHER"
,
new
HashSet
<>(
List
.
of
(
MotionState
.
MOTION_STATE_CLIMB_JUMP
,
// cost only once if repeated without switching state
MotionState
.
MOTION_STATE_DASH_BEFORE_SHAKE
,
// immediate one time sprint charge.
MotionState
.
MOTION_STATE_FIGHT
,
// immediate, if sustained then subsequent will be MOTION_NOTIFY
MotionState
.
MOTION_STATE_JUMP_UP_WALL_FOR_STANDBY
,
// immediate, observed when RUN/WALK->CLIMB
MotionState
.
MOTION_STATE_NOTIFY
,
// can be either cost or recover - check previous state and check skill casting
MotionState
.
MOTION_STATE_SIT_IDLE
,
// sustained, recover
MotionState
.
MOTION_STATE_JUMP
// recover
)));
put
(
"NOCOST_NORECOVER"
,
new
HashSet
<>(
List
.
of
(
MotionState
.
MOTION_STATE_LADDER_SLIP
,
// NOT OBSERVED
MotionState
.
MOTION_STATE_SLIP
,
// sustained, no cost no recover
MotionState
.
MOTION_STATE_FLY_IDLE
// NOT OBSERVED
)));
put
(
"IGNORE"
,
new
HashSet
<>(
List
.
of
(
// these states have no impact on stamina
MotionState
.
MOTION_STATE_CROUCH_IDLE
,
MotionState
.
MOTION_STATE_CROUCH_MOVE
,
MotionState
.
MOTION_STATE_CROUCH_ROLL
,
MotionState
.
MOTION_STATE_DESTROY_VEHICLE
,
MotionState
.
MOTION_STATE_FALL_ON_GROUND
,
MotionState
.
MOTION_STATE_FOLLOW_ROUTE
,
MotionState
.
MOTION_STATE_FORCE_SET_POS
,
MotionState
.
MOTION_STATE_GO_UPSTAIRS
,
MotionState
.
MOTION_STATE_JUMP_OFF_WALL
,
MotionState
.
MOTION_STATE_LADDER_IDLE
,
MotionState
.
MOTION_STATE_LADDER_MOVE
,
MotionState
.
MOTION_STATE_LAND_SPEED
,
MotionState
.
MOTION_STATE_MOVE_FAIL_ACK
,
MotionState
.
MOTION_STATE_NONE
,
MotionState
.
MOTION_STATE_NUM
,
MotionState
.
MOTION_STATE_QUEST_FORCE_DRAG
,
MotionState
.
MOTION_STATE_RESET
,
MotionState
.
MOTION_STATE_STANDBY_TO_LADDER
,
MotionState
.
MOTION_STATE_WATERFALL
)));
}};
private
final
Logger
logger
=
Grasscutter
.
getLogger
();
public
final
static
int
GlobalCharacterMaximumStamina
=
24000
;
public
final
static
int
GlobalVehicleMaxStamina
=
24000
;
private
Position
currentCoordinates
=
new
Position
(
0
,
0
,
0
);
private
Position
previousCoordinates
=
new
Position
(
0
,
0
,
0
);
private
MotionState
currentState
=
MotionState
.
MOTION_STATE_STANDBY
;
private
MotionState
previousState
=
MotionState
.
MOTION_STATE_STANDBY
;
private
Timer
sustainedStaminaHandlerTimer
;
private
GameSession
cachedSession
=
null
;
private
GameEntity
cachedEntity
=
null
;
private
int
staminaRecoverDelay
=
0
;
private
final
HashMap
<
String
,
BeforeUpdateStaminaListener
>
beforeUpdateStaminaListeners
=
new
HashMap
<>();
private
final
HashMap
<
String
,
AfterUpdateStaminaListener
>
afterUpdateStaminaListeners
=
new
HashMap
<>();
private
int
lastSkillId
=
0
;
private
int
lastSkillCasterId
=
0
;
private
boolean
lastSkillFirstTick
=
true
;
private
int
vehicleId
=
-
1
;
private
int
vehicleStamina
=
GlobalVehicleMaxStamina
;
private
static
final
HashSet
<
Integer
>
TalentMovements
=
new
HashSet
<>(
List
.
of
(
10013
,
10413
));
private
static
final
HashMap
<
Integer
,
Float
>
ClimbFoodReductionMap
=
new
HashMap
<>()
{{
// TODO: get real food id
put
(
0
,
0.8f
);
// Sample food
}};
private
static
final
HashMap
<
Integer
,
Float
>
DashFoodReductionMap
=
new
HashMap
<>()
{{
// TODO: get real food id
put
(
0
,
0.8f
);
// Sample food
}};
private
static
final
HashMap
<
Integer
,
Float
>
FlyFoodReductionMap
=
new
HashMap
<>()
{{
// TODO: get real food id
put
(
0
,
0.8f
);
// Sample food
}};
private
static
final
HashMap
<
Integer
,
Float
>
SwimFoodReductionMap
=
new
HashMap
<>()
{{
// TODO: get real food id
put
(
0
,
0.8f
);
// Sample food
}};
private
static
final
HashMap
<
Integer
,
Float
>
ClimbTalentReductionMap
=
new
HashMap
<>()
{{
put
(
262301
,
0.8f
);
}};
private
static
final
HashMap
<
Integer
,
Float
>
FlyTalentReductionMap
=
new
HashMap
<>()
{{
put
(
212301
,
0.8f
);
put
(
222301
,
0.8f
);
}};
private
static
final
HashMap
<
Integer
,
Float
>
SwimTalentReductionMap
=
new
HashMap
<>()
{{
put
(
242301
,
0.8f
);
put
(
542301
,
0.8f
);
}};
public
static
void
initialize
()
{
// TODO: Initialize foods etc.
}
public
StaminaManager
(
Player
player
)
{
this
.
player
=
player
;
}
// Accessors
public
void
setSkillCast
(
int
skillId
,
int
skillCasterId
)
{
lastSkillFirstTick
=
true
;
lastSkillId
=
skillId
;
lastSkillCasterId
=
skillCasterId
;
}
public
int
getMaxCharacterStamina
()
{
return
player
.
getProperty
(
PlayerProperty
.
PROP_MAX_STAMINA
);
}
public
int
getCurrentCharacterStamina
()
{
return
player
.
getProperty
(
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
);
}
public
int
getMaxVehicleStamina
()
{
return
GlobalVehicleMaxStamina
;
}
public
int
getCurrentVehicleStamina
()
{
return
vehicleStamina
;
}
public
boolean
registerBeforeUpdateStaminaListener
(
String
listenerName
,
BeforeUpdateStaminaListener
listener
)
{
if
(
beforeUpdateStaminaListeners
.
containsKey
(
listenerName
))
{
return
false
;
}
beforeUpdateStaminaListeners
.
put
(
listenerName
,
listener
);
return
true
;
}
public
boolean
unregisterBeforeUpdateStaminaListener
(
String
listenerName
)
{
if
(!
beforeUpdateStaminaListeners
.
containsKey
(
listenerName
))
{
return
false
;
}
beforeUpdateStaminaListeners
.
remove
(
listenerName
);
return
true
;
}
public
boolean
registerAfterUpdateStaminaListener
(
String
listenerName
,
AfterUpdateStaminaListener
listener
)
{
if
(
afterUpdateStaminaListeners
.
containsKey
(
listenerName
))
{
return
false
;
}
afterUpdateStaminaListeners
.
put
(
listenerName
,
listener
);
return
true
;
}
public
boolean
unregisterAfterUpdateStaminaListener
(
String
listenerName
)
{
if
(!
afterUpdateStaminaListeners
.
containsKey
(
listenerName
))
{
return
false
;
}
afterUpdateStaminaListeners
.
remove
(
listenerName
);
return
true
;
}
private
boolean
isPlayerMoving
()
{
float
diffX
=
currentCoordinates
.
getX
()
-
previousCoordinates
.
getX
();
float
diffY
=
currentCoordinates
.
getY
()
-
previousCoordinates
.
getY
();
float
diffZ
=
currentCoordinates
.
getZ
()
-
previousCoordinates
.
getZ
();
logger
.
trace
(
"isPlayerMoving: "
+
previousCoordinates
+
", "
+
currentCoordinates
+
", "
+
diffX
+
", "
+
diffY
+
", "
+
diffZ
);
return
Math
.
abs
(
diffX
)
>
0.3
||
Math
.
abs
(
diffY
)
>
0.2
||
Math
.
abs
(
diffZ
)
>
0.3
;
}
public
int
updateStaminaRelative
(
GameSession
session
,
Consumption
consumption
,
boolean
isCharacterStamina
)
{
int
currentStamina
=
isCharacterStamina
?
getCurrentCharacterStamina
()
:
getCurrentVehicleStamina
();
if
(
consumption
.
amount
==
0
)
{
return
currentStamina
;
}
// notify will update
for
(
Map
.
Entry
<
String
,
BeforeUpdateStaminaListener
>
listener
:
beforeUpdateStaminaListeners
.
entrySet
())
{
Consumption
overriddenConsumption
=
listener
.
getValue
().
onBeforeUpdateStamina
(
consumption
.
type
.
toString
(),
consumption
,
isCharacterStamina
);
if
((
overriddenConsumption
.
type
!=
consumption
.
type
)
&&
(
overriddenConsumption
.
amount
!=
consumption
.
amount
))
{
logger
.
debug
(
"Stamina update relative("
+
consumption
.
type
.
toString
()
+
", "
+
consumption
.
amount
+
") overridden to relative("
+
consumption
.
type
.
toString
()
+
", "
+
consumption
.
amount
+
") by: "
+
listener
.
getKey
());
return
currentStamina
;
}
}
int
maxStamina
=
isCharacterStamina
?
getMaxCharacterStamina
()
:
getMaxVehicleStamina
();
logger
.
trace
((
isCharacterStamina
?
"C "
:
"V "
)
+
currentStamina
+
"/"
+
maxStamina
+
"\t"
+
currentState
+
"\t"
+
(
isPlayerMoving
()
?
"moving"
:
" "
)
+
"\t("
+
consumption
.
type
+
","
+
consumption
.
amount
+
")"
);
int
newStamina
=
currentStamina
+
consumption
.
amount
;
if
(
newStamina
<
0
)
{
newStamina
=
0
;
}
else
if
(
newStamina
>
maxStamina
)
{
newStamina
=
maxStamina
;
}
return
setStamina
(
session
,
consumption
.
type
.
toString
(),
newStamina
,
isCharacterStamina
);
}
public
int
updateStaminaAbsolute
(
GameSession
session
,
String
reason
,
int
newStamina
,
boolean
isCharacterStamina
)
{
int
currentStamina
=
isCharacterStamina
?
getCurrentCharacterStamina
()
:
getCurrentVehicleStamina
();
// notify will update
for
(
Map
.
Entry
<
String
,
BeforeUpdateStaminaListener
>
listener
:
beforeUpdateStaminaListeners
.
entrySet
())
{
int
overriddenNewStamina
=
listener
.
getValue
().
onBeforeUpdateStamina
(
reason
,
newStamina
,
isCharacterStamina
);
if
(
overriddenNewStamina
!=
newStamina
)
{
logger
.
debug
(
"Stamina update absolute("
+
reason
+
", "
+
newStamina
+
") overridden to absolute("
+
reason
+
", "
+
newStamina
+
") by: "
+
listener
.
getKey
());
return
currentStamina
;
}
}
int
maxStamina
=
isCharacterStamina
?
getMaxCharacterStamina
()
:
getMaxVehicleStamina
();
if
(
newStamina
<
0
)
{
newStamina
=
0
;
}
else
if
(
newStamina
>
maxStamina
)
{
newStamina
=
maxStamina
;
}
return
setStamina
(
session
,
reason
,
newStamina
,
isCharacterStamina
);
}
// Returns new stamina and sends PlayerPropNotify or VehicleStaminaNotify
public
int
setStamina
(
GameSession
session
,
String
reason
,
int
newStamina
,
boolean
isCharacterStamina
)
{
// Target Player
if
(!
GAME_OPTIONS
.
staminaUsage
||
session
.
getPlayer
().
getStamina
())
{
newStamina
=
getMaxCharacterStamina
();
}
// set stamina if is character stamina
if
(
isCharacterStamina
)
{
player
.
setProperty
(
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
,
newStamina
);
session
.
send
(
new
PacketPlayerPropNotify
(
player
,
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
));
}
else
{
vehicleStamina
=
newStamina
;
session
.
send
(
new
PacketVehicleStaminaNotify
(
vehicleId
,
((
float
)
newStamina
)
/
100
));
}
// notify updated
for
(
Map
.
Entry
<
String
,
AfterUpdateStaminaListener
>
listener
:
afterUpdateStaminaListeners
.
entrySet
())
{
listener
.
getValue
().
onAfterUpdateStamina
(
reason
,
newStamina
,
isCharacterStamina
);
}
return
newStamina
;
}
// Kills avatar, removes entity and sends notification.
// TODO: Probably move this to Avatar class? since other components may also need to kill avatar.
public
void
killAvatar
(
GameSession
session
,
GameEntity
entity
,
PlayerDieType
dieType
)
{
session
.
send
(
new
PacketAvatarLifeStateChangeNotify
(
player
.
getTeamManager
().
getCurrentAvatarEntity
().
getAvatar
(),
LifeState
.
LIFE_DEAD
,
dieType
));
session
.
send
(
new
PacketLifeStateChangeNotify
(
entity
,
LifeState
.
LIFE_DEAD
,
dieType
));
entity
.
setFightProperty
(
FightProperty
.
FIGHT_PROP_CUR_HP
,
0
);
entity
.
getWorld
().
broadcastPacket
(
new
PacketEntityFightPropUpdateNotify
(
entity
,
FightProperty
.
FIGHT_PROP_CUR_HP
));
entity
.
getWorld
().
broadcastPacket
(
new
PacketLifeStateChangeNotify
(
0
,
entity
,
LifeState
.
LIFE_DEAD
));
player
.
getScene
().
removeEntity
(
entity
);
((
EntityAvatar
)
entity
).
onDeath
(
dieType
,
0
);
}
public
void
startSustainedStaminaHandler
()
{
if
(!
player
.
isPaused
()
&&
sustainedStaminaHandlerTimer
==
null
)
{
sustainedStaminaHandlerTimer
=
new
Timer
();
sustainedStaminaHandlerTimer
.
scheduleAtFixedRate
(
new
SustainedStaminaHandler
(),
0
,
200
);
logger
.
debug
(
"[MovementManager] SustainedStaminaHandlerTimer started"
);
}
}
public
void
stopSustainedStaminaHandler
()
{
if
(
sustainedStaminaHandlerTimer
!=
null
)
{
sustainedStaminaHandlerTimer
.
cancel
();
sustainedStaminaHandlerTimer
=
null
;
logger
.
debug
(
"[MovementManager] SustainedStaminaHandlerTimer stopped"
);
}
}
// Handlers
// External trigger handler
public
void
handleEvtDoSkillSuccNotify
(
GameSession
session
,
int
skillId
,
int
casterId
)
{
// Ignore if skill not cast by not current active avatar
if
(
casterId
!=
player
.
getTeamManager
().
getCurrentAvatarEntity
().
getId
())
{
return
;
}
setSkillCast
(
skillId
,
casterId
);
// Handle immediate stamina cost
Avatar
currentAvatar
=
player
.
getTeamManager
().
getCurrentAvatarEntity
().
getAvatar
();
if
(
currentAvatar
.
getAvatarData
().
getWeaponType
()
==
WeaponType
.
WEAPON_CLAYMORE
)
{
// Exclude claymore as their stamina cost starts when MixinStaminaCost gets in
return
;
}
// TODO: Differentiate normal attacks from charged attacks and exclude
// TODO: Temporary: Exclude non-claymore attacks for now
/*
if (BowAvatars.contains(currentAvatarId)
|| SwordAvatars.contains(currentAvatarId)
|| PolearmAvatars.contains(currentAvatarId)
|| CatalystAvatars.contains(currentAvatarId)
) {
return;
}
*/
//handleImmediateStamina(session, skillId);
}
public
void
handleMixinCostStamina
(
boolean
isSwim
)
{
// Talent moving and claymore avatar charged attack duration
// logger.trace("abilityMixinCostStamina: isSwim: " + isSwim + "\tlastSkill: " + lastSkillId);
if
(
lastSkillCasterId
==
player
.
getTeamManager
().
getCurrentAvatarEntity
().
getId
())
{
handleImmediateStamina
(
cachedSession
,
lastSkillId
);
}
}
public
void
handleCombatInvocationsNotify
(
@NotNull
GameSession
session
,
@NotNull
EntityMoveInfo
moveInfo
,
@NotNull
GameEntity
entity
)
{
// cache info for later use in SustainedStaminaHandler tick
cachedSession
=
session
;
cachedEntity
=
entity
;
MotionInfo
motionInfo
=
moveInfo
.
getMotionInfo
();
MotionState
motionState
=
motionInfo
.
getState
();
int
notifyEntityId
=
entity
.
getId
();
int
currentAvatarEntityId
=
session
.
getPlayer
().
getTeamManager
().
getCurrentAvatarEntity
().
getId
();
if
(
notifyEntityId
!=
currentAvatarEntityId
&&
notifyEntityId
!=
vehicleId
)
{
return
;
}
currentState
=
motionState
;
// logger.trace(currentState + "\t" + (notifyEntityId == currentAvatarEntityId ? "character" : "vehicle"));
Vector
posVector
=
motionInfo
.
getPos
();
Position
newPos
=
new
Position
(
posVector
.
getX
(),
posVector
.
getY
(),
posVector
.
getZ
());
if
(
newPos
.
getX
()
!=
0
&&
newPos
.
getY
()
!=
0
&&
newPos
.
getZ
()
!=
0
)
{
currentCoordinates
=
newPos
;
}
startSustainedStaminaHandler
();
handleImmediateStamina
(
session
,
motionState
);
}
public
void
handleVehicleInteractReq
(
GameSession
session
,
int
vehicleId
,
VehicleInteractType
vehicleInteractType
)
{
if
(
vehicleInteractType
==
VehicleInteractType
.
VEHICLE_INTERACT_TYPE_IN
)
{
this
.
vehicleId
=
vehicleId
;
// Reset character stamina here to prevent falling into water immediately on ejection if char stamina is
// close to empty when boarding.
updateStaminaAbsolute
(
session
,
"board vehicle"
,
getMaxCharacterStamina
(),
true
);
updateStaminaAbsolute
(
session
,
"board vehicle"
,
getMaxVehicleStamina
(),
false
);
}
else
{
this
.
vehicleId
=
-
1
;
}
}
// Internal handler
private
void
handleImmediateStamina
(
GameSession
session
,
@NotNull
MotionState
motionState
)
{
switch
(
motionState
)
{
case
MOTION_STATE_CLIMB:
if
(
currentState
!=
MotionState
.
MOTION_STATE_CLIMB
)
{
updateStaminaRelative
(
session
,
new
Consumption
(
ConsumptionType
.
CLIMB_START
),
true
);
}
break
;
case
MOTION_STATE_DASH_BEFORE_SHAKE:
if
(
previousState
!=
MotionState
.
MOTION_STATE_DASH_BEFORE_SHAKE
)
{
updateStaminaRelative
(
session
,
new
Consumption
(
ConsumptionType
.
SPRINT
),
true
);
}
break
;
case
MOTION_STATE_CLIMB_JUMP:
if
(
previousState
!=
MotionState
.
MOTION_STATE_CLIMB_JUMP
)
{
updateStaminaRelative
(
session
,
new
Consumption
(
ConsumptionType
.
CLIMB_JUMP
),
true
);
}
break
;
case
MOTION_STATE_SWIM_DASH:
if
(
previousState
!=
MotionState
.
MOTION_STATE_SWIM_DASH
)
{
updateStaminaRelative
(
session
,
new
Consumption
(
ConsumptionType
.
SWIM_DASH_START
),
true
);
}
break
;
}
}
private
void
handleImmediateStamina
(
GameSession
session
,
int
skillId
)
{
Consumption
consumption
=
getFightConsumption
(
skillId
);
updateStaminaRelative
(
session
,
consumption
,
true
);
}
private
class
SustainedStaminaHandler
extends
TimerTask
{
public
void
run
()
{
boolean
moving
=
isPlayerMoving
();
int
currentCharacterStamina
=
getCurrentCharacterStamina
();
int
maxCharacterStamina
=
getMaxCharacterStamina
();
int
currentVehicleStamina
=
getCurrentVehicleStamina
();
int
maxVehicleStamina
=
getMaxVehicleStamina
();
if
(
moving
||
(
currentCharacterStamina
<
maxCharacterStamina
)
||
(
currentVehicleStamina
<
maxVehicleStamina
))
{
logger
.
trace
(
"Player moving: "
+
moving
+
", stamina full: "
+
(
currentCharacterStamina
>=
maxCharacterStamina
)
+
", recalculate stamina"
);
boolean
isCharacterStamina
=
true
;
Consumption
consumption
;
if
(
MotionStatesCategorized
.
get
(
"CLIMB"
).
contains
(
currentState
))
{
consumption
=
getClimbConsumption
();
}
else
if
(
MotionStatesCategorized
.
get
(
"DASH"
).
contains
(
currentState
))
{
consumption
=
getDashConsumption
();
}
else
if
(
MotionStatesCategorized
.
get
(
"FLY"
).
contains
(
currentState
))
{
consumption
=
getFlyConsumption
();
}
else
if
(
MotionStatesCategorized
.
get
(
"RUN"
).
contains
(
currentState
))
{
consumption
=
new
Consumption
(
ConsumptionType
.
RUN
);
}
else
if
(
MotionStatesCategorized
.
get
(
"SKIFF"
).
contains
(
currentState
))
{
consumption
=
getSkiffConsumption
();
isCharacterStamina
=
false
;
}
else
if
(
MotionStatesCategorized
.
get
(
"STANDBY"
).
contains
(
currentState
))
{
consumption
=
new
Consumption
(
ConsumptionType
.
STANDBY
);
}
else
if
(
MotionStatesCategorized
.
get
(
"SWIM"
).
contains
(
currentState
))
{
consumption
=
getSwimConsumptions
();
}
else
if
(
MotionStatesCategorized
.
get
(
"WALK"
).
contains
(
currentState
))
{
consumption
=
new
Consumption
(
ConsumptionType
.
WALK
);
}
else
if
(
MotionStatesCategorized
.
get
(
"NOCOST_NORECOVER"
).
contains
(
currentState
))
{
consumption
=
new
Consumption
();
}
else
if
(
MotionStatesCategorized
.
get
(
"OTHER"
).
contains
(
currentState
))
{
consumption
=
getOtherConsumptions
();
}
else
{
// ignore
return
;
}
if
(
consumption
.
amount
<
0
&&
isCharacterStamina
)
{
// Do not apply reduction factor when recovering stamina
if
(
player
.
getTeamManager
().
getTeamResonances
().
contains
(
10301
))
{
consumption
.
amount
*=
0.85f
;
}
}
// Delay 1 seconds before starts recovering stamina
if
(
consumption
.
amount
!=
0
&&
cachedSession
!=
null
)
{
if
(
consumption
.
amount
<
0
)
{
staminaRecoverDelay
=
0
;
}
if
(
consumption
.
amount
>
0
&&
consumption
.
type
!=
ConsumptionType
.
POWERED_FLY
&&
consumption
.
type
!=
ConsumptionType
.
POWERED_SKIFF
)
{
// For POWERED_* recover immediately - things like Amber's gliding exam and skiff challenges may require this.
if
(
staminaRecoverDelay
<
5
)
{
// For others recover after 1 seconds (5 ticks) - as official server does.
staminaRecoverDelay
++;
consumption
.
amount
=
0
;
logger
.
trace
(
"Delaying recovery: "
+
staminaRecoverDelay
);
}
}
updateStaminaRelative
(
cachedSession
,
consumption
,
isCharacterStamina
);
}
}
previousState
=
currentState
;
previousCoordinates
=
new
Position
(
currentCoordinates
.
getX
(),
currentCoordinates
.
getY
(),
currentCoordinates
.
getZ
()
);
}
}
private
void
handleDrowning
()
{
// TODO: fix drowning waverider entity
int
stamina
=
getCurrentCharacterStamina
();
if
(
stamina
<
10
)
{
logger
.
trace
(
getCurrentCharacterStamina
()
+
"/"
+
getMaxCharacterStamina
()
+
"\t"
+
currentState
);
if
(
currentState
!=
MotionState
.
MOTION_STATE_SWIM_IDLE
)
{
killAvatar
(
cachedSession
,
cachedEntity
,
PlayerDieType
.
PLAYER_DIE_TYPE_DRAWN
);
}
}
}
// Consumption Calculators
// Stamina Consumption Reduction: https://genshin-impact.fandom.com/wiki/Stamina
private
Consumption
getFightConsumption
(
int
skillCasting
)
{
// Talent moving
if
(
TalentMovements
.
contains
(
skillCasting
))
{
// TODO: recover 1000 if kamisato hits an enemy at the end of dashing
return
getTalentMovingSustainedCost
(
skillCasting
);
}
// Bow avatar charged attack
Avatar
currentAvatar
=
player
.
getTeamManager
().
getCurrentAvatarEntity
().
getAvatar
();
switch
(
currentAvatar
.
getAvatarData
().
getWeaponType
())
{
case
WEAPON_BOW:
return
getBowSustainedCost
(
skillCasting
);
case
WEAPON_CLAYMORE:
return
getClaymoreSustainedCost
(
skillCasting
);
case
WEAPON_CATALYST:
return
getCatalystCost
(
skillCasting
);
case
WEAPON_POLE:
return
getPolearmCost
(
skillCasting
);
case
WEAPON_SWORD_ONE_HAND:
return
getSwordCost
(
skillCasting
);
}
return
new
Consumption
();
}
private
Consumption
getClimbConsumption
()
{
Consumption
consumption
=
new
Consumption
();
if
(
currentState
==
MotionState
.
MOTION_STATE_CLIMB
&&
isPlayerMoving
())
{
consumption
.
type
=
ConsumptionType
.
CLIMBING
;
consumption
.
amount
=
ConsumptionType
.
CLIMBING
.
amount
;
}
// Climbing specific reductions
consumption
.
amount
*=
getFoodCostReductionFactor
(
ClimbFoodReductionMap
);
consumption
.
amount
*=
getTalentCostReductionFactor
(
ClimbTalentReductionMap
);
return
consumption
;
}
private
Consumption
getSwimConsumptions
()
{
handleDrowning
();
Consumption
consumption
=
new
Consumption
();
if
(
currentState
==
MotionState
.
MOTION_STATE_SWIM_MOVE
)
{
consumption
.
type
=
ConsumptionType
.
SWIMMING
;
consumption
.
amount
=
ConsumptionType
.
SWIMMING
.
amount
;
}
if
(
currentState
==
MotionState
.
MOTION_STATE_SWIM_DASH
)
{
consumption
.
type
=
ConsumptionType
.
SWIM_DASH
;
consumption
.
amount
=
ConsumptionType
.
SWIM_DASH
.
amount
;
}
// Swimming specific reductions
consumption
.
amount
*=
getFoodCostReductionFactor
(
SwimFoodReductionMap
);
consumption
.
amount
*=
getTalentCostReductionFactor
(
SwimTalentReductionMap
);
return
consumption
;
}
private
Consumption
getDashConsumption
()
{
Consumption
consumption
=
new
Consumption
();
if
(
currentState
==
MotionState
.
MOTION_STATE_DASH
)
{
consumption
.
type
=
ConsumptionType
.
DASH
;
consumption
.
amount
=
ConsumptionType
.
DASH
.
amount
;
// Dashing specific reductions
consumption
.
amount
*=
getFoodCostReductionFactor
(
DashFoodReductionMap
);
}
return
consumption
;
}
private
Consumption
getFlyConsumption
()
{
// POWERED_FLY, e.g. wind tunnel
if
(
currentState
==
MotionState
.
MOTION_STATE_POWERED_FLY
)
{
return
new
Consumption
(
ConsumptionType
.
POWERED_FLY
);
}
Consumption
consumption
=
new
Consumption
(
ConsumptionType
.
FLY
);
// Flying specific reductions
consumption
.
amount
*=
getFoodCostReductionFactor
(
FlyFoodReductionMap
);
consumption
.
amount
*=
getTalentCostReductionFactor
(
FlyTalentReductionMap
);
return
consumption
;
}
private
Consumption
getSkiffConsumption
()
{
// No known reduction for skiffing.
return
switch
(
currentState
)
{
case
MOTION_STATE_SKIFF_DASH
->
new
Consumption
(
ConsumptionType
.
SKIFF_DASH
);
case
MOTION_STATE_SKIFF_POWERED_DASH
->
new
Consumption
(
ConsumptionType
.
POWERED_SKIFF
);
case
MOTION_STATE_SKIFF_NORMAL
->
new
Consumption
(
ConsumptionType
.
SKIFF
);
default
->
new
Consumption
();
};
}
private
Consumption
getOtherConsumptions
()
{
switch
(
currentState
)
{
case
MOTION_STATE_NOTIFY:
// if (BowSkills.contains(lastSkillId)) {
// return new Consumption(ConsumptionType.FIGHT, 500);
// }
break
;
case
MOTION_STATE_FIGHT:
// TODO: what if charged attack
return
new
Consumption
(
ConsumptionType
.
FIGHT
,
500
);
}
return
new
Consumption
();
}
// Reduction getter
private
float
getTalentCostReductionFactor
(
HashMap
<
Integer
,
Float
>
talentReductionMap
)
{
// All known talents reductions are not stackable
float
reduction
=
1
;
for
(
EntityAvatar
entity
:
cachedSession
.
getPlayer
().
getTeamManager
().
getActiveTeam
())
{
for
(
int
skillId
:
entity
.
getAvatar
().
getProudSkillList
())
{
if
(
talentReductionMap
.
containsKey
(
skillId
))
{
float
potentialLowerReduction
=
talentReductionMap
.
get
(
skillId
);
if
(
potentialLowerReduction
<
reduction
)
{
reduction
=
potentialLowerReduction
;
}
}
}
}
return
reduction
;
}
private
float
getFoodCostReductionFactor
(
HashMap
<
Integer
,
Float
>
foodReductionMap
)
{
// All known food reductions are not stackable
// TODO: Check consumed food (buff?) and return proper factor
float
reduction
=
1
;
return
reduction
;
}
private
Consumption
getTalentMovingSustainedCost
(
int
skillId
)
{
if
(
lastSkillFirstTick
)
{
lastSkillFirstTick
=
false
;
return
new
Consumption
(
ConsumptionType
.
TALENT_DASH
,
-
1000
);
}
else
{
return
new
Consumption
(
ConsumptionType
.
TALENT_DASH
,
-
500
);
}
}
private
Consumption
getBowSustainedCost
(
int
skillId
)
{
// Note that bow skills actually recovers stamina
// Character specific handling
// switch (skillId) {
// // No known bow skills cost stamina
// }
return
new
Consumption
(
ConsumptionType
.
FIGHT
,
+
500
);
}
private
Consumption
getCatalystCost
(
int
skillId
)
{
Consumption
consumption
=
new
Consumption
(
ConsumptionType
.
FIGHT
,
-
5000
);
// Character specific handling
switch
(
skillId
)
{
// TODO:
}
return
consumption
;
}
private
Consumption
getClaymoreSustainedCost
(
int
skillId
)
{
Consumption
consumption
=
new
Consumption
(
ConsumptionType
.
FIGHT
,
-
1333
);
// 4000 / 3 = 1333
// Character specific handling
switch
(
skillId
)
{
case
10571
:
case
10532
:
consumption
.
amount
=
0
;
break
;
case
10160
:
if
(
player
.
getTeamManager
().
getCurrentAvatarEntity
().
getAvatar
().
getProudSkillList
().
contains
(
162101
))
{
consumption
.
amount
/=
2
;
}
break
;
}
return
consumption
;
}
private
Consumption
getPolearmCost
(
int
skillId
)
{
Consumption
consumption
=
new
Consumption
(
ConsumptionType
.
FIGHT
,
-
2500
);
// Character specific handling
switch
(
skillId
)
{
// TODO:
}
return
consumption
;
}
private
Consumption
getSwordCost
(
int
skillId
)
{
Consumption
consumption
=
new
Consumption
(
ConsumptionType
.
FIGHT
,
-
2000
);
// Character specific handling
switch
(
skillId
)
{
case
10421
:
consumption
.
amount
=
-
2500
;
break
;
}
return
consumption
;
}
}
src/main/java/emu/grasscutter/game/player/InvokeHandler.java
View file @
a8293102
...
...
@@ -21,16 +21,16 @@ public class InvokeHandler<T> {
public
synchronized
void
addEntry
(
ForwardType
forward
,
T
entry
)
{
switch
(
forward
)
{
case
FORWARD_TO_ALL
->
entryListForwardAll
.
add
(
entry
);
case
FORWARD_TO_ALL_EXCEPT_CUR
,
FORWARD_TO_ALL_EXIST_EXCEPT_CUR
->
entryListForwardAllExceptCur
.
add
(
entry
);
case
FORWARD_TO_HOST
->
entryListForwardHost
.
add
(
entry
);
case
FORWARD_
TYPE_
TO_ALL
->
entryListForwardAll
.
add
(
entry
);
case
FORWARD_
TYPE_
TO_ALL_EXCEPT_CUR
,
FORWARD_
TYPE_
TO_ALL_EXIST_EXCEPT_CUR
->
entryListForwardAllExceptCur
.
add
(
entry
);
case
FORWARD_
TYPE_
TO_HOST
->
entryListForwardHost
.
add
(
entry
);
default
->
{
}
}
}
public
synchronized
void
update
(
Player
player
)
{
if
(
player
.
getWorld
()
==
null
)
{
if
(
player
.
getWorld
()
==
null
||
player
.
getScene
()
==
null
)
{
this
.
entryListForwardAll
.
clear
();
this
.
entryListForwardAllExceptCur
.
clear
();
this
.
entryListForwardHost
.
clear
();
...
...
src/main/java/emu/grasscutter/game/player/Player.java
View file @
a8293102
...
...
@@ -4,13 +4,17 @@ import dev.morphia.annotations.*;
import
emu.grasscutter.GameConstants
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.data.GameData
;
import
emu.grasscutter.data.
def
.PlayerLevelData
;
import
emu.grasscutter.data.
excels
.PlayerLevelData
;
import
emu.grasscutter.database.DatabaseHelper
;
import
emu.grasscutter.game.Account
;
import
emu.grasscutter.game.CoopRequest
;
import
emu.grasscutter.game.ability.AbilityManager
;
import
emu.grasscutter.game.avatar.Avatar
;
import
emu.grasscutter.game.avatar.AvatarProfileData
;
import
emu.grasscutter.game.avatar.AvatarStorage
;
import
emu.grasscutter.game.entity.EntityMonster
;
import
emu.grasscutter.game.entity.EntityVehicle
;
import
emu.grasscutter.game.managers.DeforestationManager.DeforestationManager
;
import
emu.grasscutter.game.entity.EntityGadget
;
import
emu.grasscutter.game.entity.EntityItem
;
import
emu.grasscutter.game.entity.GameEntity
;
...
...
@@ -22,12 +26,15 @@ import emu.grasscutter.game.inventory.GameItem;
import
emu.grasscutter.game.inventory.Inventory
;
import
emu.grasscutter.game.mail.Mail
;
import
emu.grasscutter.game.mail.MailHandler
;
import
emu.grasscutter.game.managers.MovementManager.MovementManager
;
import
emu.grasscutter.game.managers.SotSManager.SotSManager
;
import
emu.grasscutter.game.managers.InsectCaptureManager
;
import
emu.grasscutter.game.managers.StaminaManager.StaminaManager
;
import
emu.grasscutter.game.managers.SotSManager
;
import
emu.grasscutter.game.managers.EnergyManager.EnergyManager
;
import
emu.grasscutter.game.props.ActionReason
;
import
emu.grasscutter.game.props.EntityType
;
import
emu.grasscutter.game.props.PlayerProperty
;
import
emu.grasscutter.game.props.SceneType
;
import
emu.grasscutter.game.quest.QuestManager
;
import
emu.grasscutter.game.shop.ShopLimit
;
import
emu.grasscutter.game.managers.MapMarkManager.*
;
import
emu.grasscutter.game.tower.TowerManager
;
...
...
@@ -44,10 +51,12 @@ import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo;
import
emu.grasscutter.net.proto.PlayerLocationInfoOuterClass.PlayerLocationInfo
;
import
emu.grasscutter.net.proto.ProfilePictureOuterClass.ProfilePicture
;
import
emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail
;
import
emu.grasscutter.server.event.player.PlayerJoinEvent
;
import
emu.grasscutter.server.event.player.PlayerQuitEvent
;
import
emu.grasscutter.server.game.GameServer
;
import
emu.grasscutter.server.game.GameSession
;
import
emu.grasscutter.server.game.GameSession.SessionState
;
import
emu.grasscutter.server.packet.send.*
;
import
emu.grasscutter.utils.DateHelper
;
import
emu.grasscutter.utils.Position
;
...
...
@@ -59,12 +68,11 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import
java.util.*
;
import
java.util.concurrent.LinkedBlockingQueue
;
import
static
emu
.
grasscutter
.
Configuration
.*;
@Entity
(
value
=
"players"
,
useDiscriminator
=
false
)
public
class
Player
{
@Transient
private
static
int
GlobalMaximumSpringVolume
=
8500000
;
@Transient
private
static
int
GlobalMaximumStamina
=
24000
;
@Id
private
int
id
;
@Indexed
(
options
=
@IndexOptions
(
unique
=
true
))
private
String
accountId
;
...
...
@@ -76,11 +84,18 @@ public class Player {
private
Position
pos
;
private
Position
rotation
;
private
PlayerBirthday
birthday
;
private
PlayerCodex
codex
;
private
Map
<
Integer
,
Integer
>
properties
;
private
Set
<
Integer
>
nameCardList
;
private
Set
<
Integer
>
flyCloakList
;
private
Set
<
Integer
>
costumeList
;
private
Set
<
Integer
>
unlockedForgingBlueprints
;
private
Integer
widgetId
;
private
Set
<
Integer
>
realmList
;
private
Integer
currentRealmId
;
@Transient
private
long
nextGuid
=
0
;
@Transient
private
int
peerId
;
...
...
@@ -92,8 +107,11 @@ public class Player {
@Transient
private
FriendsList
friendsList
;
@Transient
private
MailHandler
mailHandler
;
@Transient
private
MessageHandler
messageHandler
;
@Transient
private
AbilityManager
abilityManager
;
@Transient
private
QuestManager
questManager
;
@Transient
private
SotSManager
sotsManager
;
@Transient
private
InsectCaptureManager
insectCaptureManager
;
private
TeamManager
teamManager
;
...
...
@@ -111,6 +129,7 @@ public class Player {
private
int
mainCharacterId
;
private
boolean
godmode
;
private
boolean
stamina
;
private
boolean
moonCard
;
private
Date
moonCardStartTime
;
private
int
moonCardDuration
;
...
...
@@ -131,11 +150,13 @@ public class Player {
@Transient
private
final
InvokeHandler
<
AbilityInvokeEntry
>
abilityInvokeHandler
;
@Transient
private
final
InvokeHandler
<
AbilityInvokeEntry
>
clientAbilityInitFinishHandler
;
private
MapMarksManager
mapMarksManager
;
@Transient
private
MovementManager
movementManager
;
@Transient
private
MapMarksManager
mapMarksManager
;
@Transient
private
StaminaManager
staminaManager
;
@Transient
private
EnergyManager
energyManager
;
@Transient
private
DeforestationManager
deforestationManager
;
private
long
springLastUsed
;
private
HashMap
<
String
,
MapMark
>
mapMarks
;
@Deprecated
@SuppressWarnings
({
"rawtypes"
,
"unchecked"
})
// Morphia only!
...
...
@@ -145,6 +166,11 @@ public class Player {
this
.
friendsList
=
new
FriendsList
(
this
);
this
.
mailHandler
=
new
MailHandler
(
this
);
this
.
towerManager
=
new
TowerManager
(
this
);
this
.
abilityManager
=
new
AbilityManager
(
this
);
this
.
deforestationManager
=
new
DeforestationManager
(
this
);
this
.
insectCaptureManager
=
new
InsectCaptureManager
(
this
);
this
.
setQuestManager
(
new
QuestManager
(
this
));
this
.
pos
=
new
Position
();
this
.
rotation
=
new
Position
();
this
.
properties
=
new
HashMap
<>();
...
...
@@ -159,6 +185,7 @@ public class Player {
this
.
nameCardList
=
new
HashSet
<>();
this
.
flyCloakList
=
new
HashSet
<>();
this
.
costumeList
=
new
HashSet
<>();
this
.
unlockedForgingBlueprints
=
new
HashSet
<>();
this
.
setSceneId
(
3
);
this
.
setRegionId
(
1
);
...
...
@@ -173,13 +200,15 @@ public class Player {
this
.
birthday
=
new
PlayerBirthday
();
this
.
rewardedLevels
=
new
HashSet
<>();
this
.
moonCardGetTimes
=
new
HashSet
<>();
this
.
codex
=
new
PlayerCodex
(
this
);
this
.
shopLimit
=
new
ArrayList
<>();
this
.
expeditionInfo
=
new
HashMap
<>();
this
.
messageHandler
=
null
;
this
.
mapMarksManager
=
new
MapMarksManager
();
this
.
movement
Manager
=
new
Movement
Manager
(
this
);
this
.
mapMarksManager
=
new
MapMarksManager
(
this
);
this
.
stamina
Manager
=
new
Stamina
Manager
(
this
);
this
.
sotsManager
=
new
SotSManager
(
this
);
this
.
energyManager
=
new
EnergyManager
(
this
);
}
// On player creation
...
...
@@ -192,6 +221,7 @@ public class Player {
this
.
signature
=
""
;
this
.
teamManager
=
new
TeamManager
(
this
);
this
.
birthday
=
new
PlayerBirthday
();
this
.
codex
=
new
PlayerCodex
(
this
);
this
.
setProperty
(
PlayerProperty
.
PROP_PLAYER_LEVEL
,
1
);
this
.
setProperty
(
PlayerProperty
.
PROP_IS_SPRING_AUTO_USE
,
1
);
this
.
setProperty
(
PlayerProperty
.
PROP_SPRING_AUTO_USE_PERCENT
,
50
);
...
...
@@ -205,9 +235,11 @@ public class Player {
this
.
getPos
().
set
(
GameConstants
.
START_POSITION
);
this
.
getRotation
().
set
(
0
,
307
,
0
);
this
.
messageHandler
=
null
;
this
.
mapMarksManager
=
new
MapMarksManager
();
this
.
movement
Manager
=
new
Movement
Manager
(
this
);
this
.
mapMarksManager
=
new
MapMarksManager
(
this
);
this
.
stamina
Manager
=
new
Stamina
Manager
(
this
);
this
.
sotsManager
=
new
SotSManager
(
this
);
this
.
energyManager
=
new
EnergyManager
(
this
);
this
.
deforestationManager
=
new
DeforestationManager
(
this
);
}
public
int
getUid
()
{
...
...
@@ -229,7 +261,6 @@ public class Player {
public
void
setAccount
(
Account
account
)
{
this
.
account
=
account
;
this
.
account
.
setPlayerId
(
getUid
());
}
public
GameSession
getSession
()
{
...
...
@@ -295,6 +326,39 @@ public class Player {
this
.
updateProfile
();
}
public
Integer
getWidgetId
()
{
return
widgetId
;
}
public
void
setWidgetId
(
Integer
widgetId
)
{
this
.
widgetId
=
widgetId
;
}
public
Set
<
Integer
>
getRealmList
()
{
return
realmList
;
}
public
void
setRealmList
(
Set
<
Integer
>
realmList
)
{
this
.
realmList
=
realmList
;
}
public
void
addRealmList
(
int
realmId
)
{
if
(
this
.
realmList
==
null
)
{
this
.
realmList
=
new
HashSet
<>();
}
else
if
(
this
.
realmList
.
contains
(
realmId
))
{
return
;
}
this
.
realmList
.
add
(
realmId
);
}
public
Integer
getCurrentRealmId
()
{
return
currentRealmId
;
}
public
void
setCurrentRealmId
(
Integer
currentRealmId
)
{
this
.
currentRealmId
=
currentRealmId
;
}
public
Position
getPos
()
{
return
pos
;
}
...
...
@@ -353,7 +417,7 @@ public class Player {
}
private
float
getExpModifier
()
{
return
G
rasscutter
.
getConfig
().
getGameServerOptions
().
getGameRates
().
ADVENTURE_EXP_RATE
;
return
G
AME_OPTIONS
.
rates
.
adventureExp
;
}
// Affected by exp rate
...
...
@@ -409,6 +473,14 @@ public class Player {
return
towerManager
;
}
public
QuestManager
getQuestManager
()
{
return
questManager
;
}
public
void
setQuestManager
(
QuestManager
questManager
)
{
this
.
questManager
=
questManager
;
}
public
PlayerGachaInfo
getGachaInfo
()
{
return
gachaInfo
;
}
...
...
@@ -446,8 +518,12 @@ public class Player {
return
this
.
nameCardList
;
}
public
Set
<
Integer
>
getUnlockedForgingBlueprints
()
{
return
unlockedForgingBlueprints
;
}
public
MpSettingType
getMpSetting
()
{
return
MpSettingType
.
MP_SETTING_ENTER_AFTER_APPLY
;
// TEMP
return
MpSettingType
.
MP_SETTING_
TYPE_
ENTER_AFTER_APPLY
;
// TEMP
}
public
Queue
<
AttackResult
>
getAttackResults
()
{
...
...
@@ -699,7 +775,6 @@ public class Player {
return
expeditionInfo
.
get
(
avaterGuid
);
}
public
List
<
ShopLimit
>
getShopLimit
()
{
return
shopLimit
;
}
...
...
@@ -727,7 +802,14 @@ public class Player {
}
this
.
save
();
}
public
boolean
getStamina
()
{
// Get Stamina
return
stamina
;
}
public
void
setStamina
(
boolean
stamina
)
{
// Set Stamina
this
.
stamina
=
stamina
;
}
public
boolean
inGodmode
()
{
return
godmode
;
}
...
...
@@ -744,7 +826,7 @@ public class Player {
this
.
hasSentAvatarDataNotify
=
hasSentAvatarDataNotify
;
}
public
void
addAvatar
(
Avatar
avatar
)
{
public
void
addAvatar
(
Avatar
avatar
,
boolean
addToCurrentTeam
)
{
boolean
result
=
getAvatars
().
addAvatar
(
avatar
);
if
(
result
)
{
...
...
@@ -755,14 +837,22 @@ public class Player {
if
(
hasSentAvatarDataNotify
())
{
// Recalc stats
avatar
.
recalcStats
();
// Packet
sendPacket
(
new
PacketAvatarAddNotify
(
avatar
,
false
));
// Packet, show notice on left if the avatar will be added to the team
sendPacket
(
new
PacketAvatarAddNotify
(
avatar
,
addToCurrentTeam
&&
this
.
getTeamManager
().
canAddAvatarToCurrentTeam
()));
if
(
addToCurrentTeam
)
{
// If space in team, add
this
.
getTeamManager
().
addAvatarToCurrentTeam
(
avatar
);
}
}
}
else
{
// Failed adding avatar
}
}
public
void
addAvatar
(
Avatar
avatar
)
{
addAvatar
(
avatar
,
true
);
}
public
void
addFlycloak
(
int
flycloakId
)
{
this
.
getFlyCloakList
().
add
(
flycloakId
);
this
.
sendPacket
(
new
PacketAvatarGainFlycloakNotify
(
flycloakId
));
...
...
@@ -830,63 +920,59 @@ public class Player {
public
void
interactWith
(
int
gadgetEntityId
)
{
GameEntity
entity
=
getScene
().
getEntityById
(
gadgetEntityId
);
if
(
entity
==
null
)
{
return
;
}
// Handle
if
(
entity
instanceof
EntityItem
)
{
if
(
entity
instanceof
EntityItem
drop
)
{
// Pick item
EntityItem
drop
=
(
EntityItem
)
entity
;
if
(!
drop
.
isShare
())
// check drop owner to avoid someone picked up item in others' world
{
int
dropOwner
=
(
int
)(
drop
.
getGuid
()
>>
32
);
if
(
dropOwner
!=
getUid
())
if
(
dropOwner
!=
getUid
())
{
return
;
}
}
entity
.
getScene
().
removeEntity
(
entity
);
GameItem
item
=
new
GameItem
(
drop
.
getItemData
(),
drop
.
getCount
());
// Add to inventory
boolean
success
=
getInventory
().
addItem
(
item
,
ActionReason
.
SubfieldDrop
);
if
(
success
)
{
if
(!
drop
.
isShare
())
// not shared drop
this
.
sendPacket
(
new
PacketGadgetInteractRsp
(
drop
,
InteractType
.
INTERACT_PICK_ITEM
));
else
this
.
getScene
().
broadcastPacket
(
new
PacketGadgetInteractRsp
(
drop
,
InteractType
.
INTERACT_PICK_ITEM
));
if
(!
drop
.
isShare
())
{
// not shared drop
this
.
sendPacket
(
new
PacketGadgetInteractRsp
(
drop
,
InteractType
.
INTERACT_TYPE_PICK_ITEM
));
}
else
{
this
.
getScene
().
broadcastPacket
(
new
PacketGadgetInteractRsp
(
drop
,
InteractType
.
INTERACT_TYPE_PICK_ITEM
));
}
}
else
if
(
entity
instanceof
EntityGadget
)
{
EntityGadget
gadget
=
(
EntityGadget
)
entity
;
}
}
else
if
(
entity
instanceof
EntityGadget
gadget
)
{
if
(
gadget
.
getGadgetData
().
getType
()
==
EntityType
.
RewardStatue
)
{
if
(
scene
.
getChallenge
()
!=
null
)
{
scene
.
getChallenge
().
getStatueDrops
(
this
);
}
this
.
sendPacket
(
new
PacketGadgetInteractRsp
(
gadget
,
InteractType
.
INTERACT_OPEN_STATUE
));
this
.
sendPacket
(
new
PacketGadgetInteractRsp
(
gadget
,
InteractType
.
INTERACT_TYPE_OPEN_STATUE
));
}
}
else
if
(
entity
instanceof
EntityMonster
monster
)
{
insectCaptureManager
.
arrestSmallCreature
(
monster
);
}
else
if
(
entity
instanceof
EntityVehicle
vehicle
)
{
// try to arrest it, example: glowworm
insectCaptureManager
.
arrestSmallCreature
(
vehicle
);
}
else
{
// Delete directly
entity
.
getScene
().
removeEntity
(
entity
);
}
return
;
}
public
void
onPause
()
{
getStaminaManager
().
stopSustainedStaminaHandler
();
}
public
void
onUnpause
()
{
getStaminaManager
().
startSustainedStaminaHandler
();
}
public
void
sendPacket
(
BasePacket
packet
)
{
if
(
this
.
hasSentAvatarDataNotify
)
{
this
.
getSession
().
send
(
packet
);
}
}
public
OnlinePlayerInfo
getOnlinePlayerInfo
()
{
OnlinePlayerInfo
.
Builder
onlineInfo
=
OnlinePlayerInfo
.
newBuilder
()
...
...
@@ -920,6 +1006,8 @@ public class Player {
return
this
.
birthday
.
getDay
()
>
0
;
}
public
PlayerCodex
getCodex
(){
return
this
.
codex
;
}
public
Set
<
Integer
>
getRewardedLevels
()
{
return
rewardedLevels
;
}
...
...
@@ -944,8 +1032,8 @@ public class Player {
}
}
}
else
{
List
<
Integer
>
showAvatarList
=
DatabaseHelper
.
getPlayerBy
I
d
(
id
).
getShowAvatarList
();
AvatarStorage
avatars
=
DatabaseHelper
.
getPlayerBy
I
d
(
id
).
getAvatars
();
List
<
Integer
>
showAvatarList
=
DatabaseHelper
.
getPlayerBy
Ui
d
(
id
).
getShowAvatarList
();
AvatarStorage
avatars
=
DatabaseHelper
.
getPlayerBy
Ui
d
(
id
).
getAvatars
();
avatars
.
loadFromDatabase
();
if
(
showAvatarList
!=
null
)
{
for
(
int
avatarId
:
showAvatarList
)
{
...
...
@@ -985,7 +1073,7 @@ public class Player {
player
=
this
;
shouldRecalc
=
false
;
}
else
{
player
=
DatabaseHelper
.
getPlayerBy
I
d
(
id
);
player
=
DatabaseHelper
.
getPlayerBy
Ui
d
(
id
);
player
.
getAvatars
().
loadFromDatabase
();
player
.
getInventory
().
loadFromDatabase
();
shouldRecalc
=
true
;
...
...
@@ -1024,10 +1112,26 @@ public class Player {
return
mapMarksManager
;
}
public
Movement
Manager
get
Movement
Manager
()
{
return
movement
Manager
;
}
public
Stamina
Manager
get
Stamina
Manager
()
{
return
stamina
Manager
;
}
public
SotSManager
getSotSManager
()
{
return
sotsManager
;
}
public
EnergyManager
getEnergyManager
()
{
return
this
.
energyManager
;
}
public
AbilityManager
getAbilityManager
()
{
return
abilityManager
;
}
public
DeforestationManager
getDeforestationManager
()
{
return
deforestationManager
;
}
public
HashMap
<
String
,
MapMark
>
getMapMarks
()
{
return
mapMarks
;
}
public
void
setMapMarks
(
HashMap
<
String
,
MapMark
>
newMarks
)
{
mapMarks
=
newMarks
;
}
public
synchronized
void
onTick
()
{
// Check ping
if
(
this
.
getLastPingTime
()
>
System
.
currentTimeMillis
()
+
60000
)
{
...
...
@@ -1039,7 +1143,10 @@ public class Player {
while
(
it
.
hasNext
())
{
CoopRequest
req
=
it
.
next
();
if
(
req
.
isExpired
())
{
req
.
getRequester
().
sendPacket
(
new
PacketPlayerApplyEnterMpResultNotify
(
this
,
false
,
PlayerApplyEnterMpResultNotifyOuterClass
.
PlayerApplyEnterMpResultNotify
.
Reason
.
SYSTEM_JUDGE
));
req
.
getRequester
().
sendPacket
(
new
PacketPlayerApplyEnterMpResultNotify
(
this
,
false
,
PlayerApplyEnterMpResultNotifyOuterClass
.
PlayerApplyEnterMpResultNotify
.
Reason
.
REASON_SYSTEM_JUDGE
));
it
.
remove
();
}
}
...
...
@@ -1083,6 +1190,7 @@ public class Player {
@PostLoad
private
void
onLoad
()
{
this
.
getCodex
().
setPlayer
(
this
);
this
.
getTeamManager
().
setPlayer
(
this
);
this
.
getTowerManager
().
setPlayer
(
this
);
}
...
...
@@ -1091,22 +1199,19 @@ public class Player {
DatabaseHelper
.
savePlayer
(
this
);
}
public
void
onLogin
()
{
// Called from tokenrsp
public
void
loadFromDatabase
()
{
// Make sure these exist
if
(
this
.
getTeamManager
()
==
null
)
{
this
.
teamManager
=
new
TeamManager
(
this
);
}
if
(
this
.
getCodex
()
==
null
)
{
this
.
codex
=
new
PlayerCodex
(
this
);
}
if
(
this
.
getProfile
().
getUid
()
==
0
)
{
this
.
getProfile
().
syncWithCharacter
(
this
);
}
// Check if player object exists in server
// TODO - optimize
Player
exists
=
this
.
getServer
().
getPlayerByUid
(
getUid
());
if
(
exists
!=
null
)
{
exists
.
getSession
().
close
();
}
// Load from db
this
.
getAvatars
().
loadFromDatabase
();
this
.
getInventory
().
loadFromDatabase
();
...
...
@@ -1114,17 +1219,36 @@ public class Player {
this
.
getFriendsList
().
loadFromDatabase
();
this
.
getMailHandler
().
loadFromDatabase
();
this
.
getQuestManager
().
loadFromDatabase
();
// Create world
World
world
=
new
World
(
this
);
world
.
addPlayer
(
this
);
// Add to gameserver
// Add to gameserver (Always handle last)
if
(
getSession
().
isActive
())
{
getServer
().
registerPlayer
(
this
);
getProfile
().
setPlayer
(
this
);
// Set online
}
}
public
void
onLogin
()
{
// Quest - Commented out because a problem is caused if you log out while this quest is active
/*
if (getQuestManager().getMainQuestById(351) == null) {
GameQuest quest = getQuestManager().addQuest(35104);
if (quest != null) {
quest.finish();
}
getQuestManager().addQuest(35101);
this.setSceneId(3);
this.getPos().set(GameConstants.START_POSITION);
}
*/
// Create world
World
world
=
new
World
(
this
);
world
.
addPlayer
(
this
);
// Multiplayer setting
this
.
setProperty
(
PlayerProperty
.
PROP_PLAYER_MP_SETTING_TYPE
,
this
.
getMpSetting
().
getNumber
());
this
.
setProperty
(
PlayerProperty
.
PROP_IS_MP_MODE_AVAILABLE
,
1
);
...
...
@@ -1134,6 +1258,14 @@ public class Player {
session
.
send
(
new
PacketStoreWeightLimitNotify
());
session
.
send
(
new
PacketPlayerStoreNotify
(
this
));
session
.
send
(
new
PacketAvatarDataNotify
(
this
));
session
.
send
(
new
PacketFinishedParentQuestNotify
(
this
));
session
.
send
(
new
PacketQuestListNotify
(
this
));
session
.
send
(
new
PacketCodexDataFullNotify
(
this
));
session
.
send
(
new
PacketAllWidgetDataNotify
(
this
));
session
.
send
(
new
PacketWidgetGadgetAllDataNotify
());
session
.
send
(
new
PacketPlayerHomeCompInfoNotify
(
this
));
session
.
send
(
new
PacketHomeComfortInfoNotify
(
this
));
session
.
send
(
new
PacketForgeDataNotify
(
this
));
getTodayMoonCard
();
// The timer works at 0:0, some users log in after that, use this method to check if they have received a reward today or not. If not, send the reward.
...
...
@@ -1144,6 +1276,9 @@ public class Player {
// First notify packets sent
this
.
setHasSentAvatarDataNotify
(
true
);
// Set session state
session
.
setState
(
SessionState
.
ACTIVE
);
// Call join event.
PlayerJoinEvent
event
=
new
PlayerJoinEvent
(
this
);
event
.
call
();
if
(
event
.
isCanceled
())
// If event is not cancelled, continue.
...
...
@@ -1151,13 +1286,13 @@ public class Player {
}
public
void
onLogout
()
{
try
{
// stop stamina calculation
get
Movement
Manager
().
resetTim
er
();
get
Stamina
Manager
().
stopSustainedStaminaHandl
er
();
// force to leave the dungeon
if
(
getScene
().
getSceneType
()
==
SceneType
.
SCENE_DUNGEON
)
{
// force to leave the dungeon (inside has a "if")
this
.
getServer
().
getDungeonManager
().
exitDungeon
(
this
);
}
// Leave world
if
(
this
.
getWorld
()
!=
null
)
{
this
.
getWorld
().
removePlayer
(
this
);
...
...
@@ -1176,6 +1311,23 @@ public class Player {
// Call quit event.
PlayerQuitEvent
event
=
new
PlayerQuitEvent
(
this
);
event
.
call
();
//reset wood
getDeforestationManager
().
resetWood
();
}
catch
(
Throwable
e
){
e
.
printStackTrace
();
Grasscutter
.
getLogger
().
warn
(
"Player (UID {}) save failure"
,
getUid
());
}
finally
{
removeFromServer
();
}
}
public
void
removeFromServer
()
{
// Remove from server.
//Note: DON'T DELETE BY UID,BECAUSE THERE ARE MULTIPLE SAME UID PLAYERS WHEN DUPLICATED LOGIN!
//so I decide to delete by object rather than uid
getServer
().
getPlayers
().
values
().
removeIf
(
player1
->
player1
==
this
);
}
public
enum
SceneLoadState
{
...
...
@@ -1214,7 +1366,7 @@ public class Player {
}
else
if
(
prop
==
PlayerProperty
.
PROP_LAST_CHANGE_AVATAR_TIME
)
{
// 10001
// TODO: implement sanity check
}
else
if
(
prop
==
PlayerProperty
.
PROP_MAX_SPRING_VOLUME
)
{
// 10002
if
(!(
value
>=
0
&&
value
<=
GlobalMaximumSpringVolume
))
{
return
false
;
}
if
(!(
value
>=
0
&&
value
<=
SotSManager
.
GlobalMaximumSpringVolume
))
{
return
false
;
}
}
else
if
(
prop
==
PlayerProperty
.
PROP_CUR_SPRING_VOLUME
)
{
// 10003
int
playerMaximumSpringVolume
=
getProperty
(
PlayerProperty
.
PROP_MAX_SPRING_VOLUME
);
if
(!(
value
>=
0
&&
value
<=
playerMaximumSpringVolume
))
{
return
false
;
}
...
...
@@ -1231,7 +1383,7 @@ public class Player {
}
else
if
(
prop
==
PlayerProperty
.
PROP_IS_TRANSFERABLE
)
{
// 10009
if
(!(
0
<=
value
&&
value
<=
1
))
{
return
false
;
}
}
else
if
(
prop
==
PlayerProperty
.
PROP_MAX_STAMINA
)
{
// 10010
if
(!(
value
>=
0
&&
value
<=
Global
MaximumStamina
))
{
return
false
;
}
if
(!(
value
>=
0
&&
value
<=
StaminaManager
.
GlobalCharacter
MaximumStamina
))
{
return
false
;
}
}
else
if
(
prop
==
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
)
{
// 10011
int
playerMaximumStamina
=
getProperty
(
PlayerProperty
.
PROP_MAX_STAMINA
);
if
(!(
value
>=
0
&&
value
<=
playerMaximumStamina
))
{
return
false
;
}
...
...
@@ -1242,7 +1394,7 @@ public class Player {
}
else
if
(
prop
==
PlayerProperty
.
PROP_PLAYER_EXP
)
{
// 10014
if
(!(
0
<=
value
))
{
return
false
;
}
}
else
if
(
prop
==
PlayerProperty
.
PROP_PLAYER_HCOIN
)
{
// 10015
// see
10015
// see
PlayerProperty.PROP_PLAYER_HCOIN comments
}
else
if
(
prop
==
PlayerProperty
.
PROP_PLAYER_SCOIN
)
{
// 10016
// See 10015
}
else
if
(
prop
==
PlayerProperty
.
PROP_PLAYER_MP_SETTING_TYPE
)
{
// 10017
...
...
src/main/java/emu/grasscutter/game/player/PlayerCodex.java
0 → 100644
View file @
a8293102
package
emu.grasscutter.game.player
;
import
dev.morphia.annotations.Entity
;
import
dev.morphia.annotations.Transient
;
import
emu.grasscutter.data.GameData
;
import
emu.grasscutter.data.excels.CodexAnimalData
;
import
emu.grasscutter.data.excels.CodexReliquaryData
;
import
emu.grasscutter.game.entity.EntityMonster
;
import
emu.grasscutter.game.entity.GameEntity
;
import
emu.grasscutter.game.inventory.GameItem
;
import
emu.grasscutter.game.inventory.ItemType
;
import
emu.grasscutter.game.inventory.MaterialType
;
import
emu.grasscutter.server.packet.send.PacketCodexDataUpdateNotify
;
import
java.util.*
;
@Entity
public
class
PlayerCodex
{
@Transient
private
Player
player
;
//itemId is not codexId!
private
Set
<
Integer
>
unlockedWeapon
;
private
Map
<
Integer
,
Integer
>
unlockedAnimal
;
private
Set
<
Integer
>
unlockedMaterial
;
private
Set
<
Integer
>
unlockedBook
;
private
Set
<
Integer
>
unlockedTip
;
private
Set
<
Integer
>
unlockedView
;
private
Set
<
Integer
>
unlockedReliquary
;
private
Set
<
Integer
>
unlockedReliquarySuitCodex
;
public
PlayerCodex
(){
this
.
unlockedWeapon
=
new
HashSet
<>();
this
.
unlockedAnimal
=
new
HashMap
<>();
this
.
unlockedMaterial
=
new
HashSet
<>();
this
.
unlockedBook
=
new
HashSet
<>();
this
.
unlockedTip
=
new
HashSet
<>();
this
.
unlockedView
=
new
HashSet
<>();
this
.
unlockedReliquary
=
new
HashSet
<>();
this
.
unlockedReliquarySuitCodex
=
new
HashSet
<>();
}
public
PlayerCodex
(
Player
player
){
this
();
this
.
player
=
player
;
}
public
void
setPlayer
(
Player
player
)
{
this
.
player
=
player
;
}
public
void
checkAddedItem
(
GameItem
item
){
ItemType
type
=
item
.
getItemData
().
getItemType
();
if
(
type
==
ItemType
.
ITEM_WEAPON
){
if
(!
getUnlockedWeapon
().
contains
(
item
.
getItemId
())){
getUnlockedWeapon
().
add
(
item
.
getItemId
());
var
codexItem
=
GameData
.
getCodexWeaponDataIdMap
().
get
(
item
.
getItemId
());
if
(
codexItem
!=
null
){
player
.
save
();
this
.
player
.
sendPacket
(
new
PacketCodexDataUpdateNotify
(
2
,
codexItem
.
getId
()));
}
}
}
else
if
(
type
==
ItemType
.
ITEM_MATERIAL
){
if
(
item
.
getItemData
().
getMaterialType
()
==
MaterialType
.
MATERIAL_FOOD
||
item
.
getItemData
().
getMaterialType
()
==
MaterialType
.
MATERIAL_WIDGET
||
item
.
getItemData
().
getMaterialType
()
==
MaterialType
.
MATERIAL_EXCHANGE
||
item
.
getItemData
().
getMaterialType
()
==
MaterialType
.
MATERIAL_AVATAR_MATERIAL
||
item
.
getItemData
().
getMaterialType
()
==
MaterialType
.
MATERIAL_NOTICE_ADD_HP
){
if
(!
getUnlockedMaterial
().
contains
(
item
.
getItemId
()))
{
var
codexMaterial
=
GameData
.
getCodexMaterialDataIdMap
().
get
(
item
.
getItemId
());
if
(
codexMaterial
!=
null
)
{
getUnlockedMaterial
().
add
(
item
.
getItemId
());
player
.
save
();
this
.
player
.
sendPacket
(
new
PacketCodexDataUpdateNotify
(
4
,
codexMaterial
.
getId
()));
}
}
}
}
else
if
(
type
==
ItemType
.
ITEM_RELIQUARY
)
{
if
(!
getUnlockedReliquary
().
contains
(
item
.
getItemId
())){
getUnlockedReliquary
().
add
(
item
.
getItemId
());
checkUnlockedSuits
(
item
);
}
}
}
public
void
checkAnimal
(
GameEntity
target
,
CodexAnimalData
.
CodexAnimalUnlockCondition
condition
){
if
(
target
instanceof
EntityMonster
){
var
monsterId
=
((
EntityMonster
)
target
).
getMonsterData
().
getId
();
var
codexAnimal
=
GameData
.
getCodexAnimalDataMap
().
get
(
monsterId
);
if
(!
getUnlockedAnimal
().
containsKey
(
monsterId
))
{
if
(
codexAnimal
!=
null
)
{
if
(
codexAnimal
.
getUnlockCondition
()
==
condition
||
codexAnimal
.
getUnlockCondition
()
==
null
){
getUnlockedAnimal
().
put
(
monsterId
,
1
);
}
}
}
else
{
getUnlockedAnimal
().
put
(
monsterId
,
getUnlockedAnimal
().
get
(
monsterId
)
+
1
);
}
player
.
save
();
this
.
player
.
sendPacket
(
new
PacketCodexDataUpdateNotify
(
3
,
monsterId
));
}
}
public
void
checkUnlockedSuits
(
GameItem
item
){
int
reliquaryId
=
item
.
getItemId
();
Optional
<
CodexReliquaryData
>
excelReliquarySuitList
=
GameData
.
getcodexReliquaryArrayList
().
stream
().
filter
(
x
->
x
.
getCupId
()
==
reliquaryId
||
x
.
getLeatherId
()
==
reliquaryId
||
x
.
getCapId
()
==
reliquaryId
||
x
.
getFlowerId
()
==
reliquaryId
||
x
.
getSandId
()
==
reliquaryId
).
findFirst
();
if
(
excelReliquarySuitList
.
isPresent
())
{
var
excelReliquarySuit
=
excelReliquarySuitList
.
get
();
if
(!
getUnlockedReliquarySuitCodex
().
contains
(
excelReliquarySuit
.
getId
())){
if
(
getUnlockedReliquary
().
contains
(
excelReliquarySuit
.
getCupId
())
&&
getUnlockedReliquary
().
contains
(
excelReliquarySuit
.
getLeatherId
())
&&
getUnlockedReliquary
().
contains
(
excelReliquarySuit
.
getCapId
())
&&
getUnlockedReliquary
().
contains
(
excelReliquarySuit
.
getFlowerId
())
&&
getUnlockedReliquary
().
contains
(
excelReliquarySuit
.
getSandId
())
){
getUnlockedReliquarySuitCodex
().
add
(
excelReliquarySuit
.
getId
());
player
.
save
();
this
.
player
.
sendPacket
(
new
PacketCodexDataUpdateNotify
(
8
,
excelReliquarySuit
.
getId
()));
}
}
}
}
public
Set
<
Integer
>
getUnlockedWeapon
()
{
return
unlockedWeapon
;
}
public
Map
<
Integer
,
Integer
>
getUnlockedAnimal
()
{
return
unlockedAnimal
;
}
public
Set
<
Integer
>
getUnlockedMaterial
()
{
return
unlockedMaterial
;
}
public
Set
<
Integer
>
getUnlockedBook
()
{
return
unlockedBook
;
}
public
Set
<
Integer
>
getUnlockedTip
()
{
return
unlockedTip
;
}
public
Set
<
Integer
>
getUnlockedView
()
{
return
unlockedView
;
}
public
Set
<
Integer
>
getUnlockedReliquary
()
{
return
unlockedReliquary
;
}
public
Set
<
Integer
>
getUnlockedReliquarySuitCodex
()
{
return
unlockedReliquarySuitCodex
;
}
}
\ No newline at end of file
src/main/java/emu/grasscutter/game/player/TeamInfo.java
View file @
a8293102
...
...
@@ -4,10 +4,10 @@ import java.util.ArrayList;
import
java.util.List
;
import
dev.morphia.annotations.Entity
;
import
emu.grasscutter.GameConstants
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.game.avatar.Avatar
;
import
static
emu
.
grasscutter
.
Configuration
.*;
@Entity
public
class
TeamInfo
{
private
String
name
;
...
...
@@ -15,7 +15,7 @@ public class TeamInfo {
public
TeamInfo
()
{
this
.
name
=
""
;
this
.
avatars
=
new
ArrayList
<>(
G
rasscutter
.
getConfig
().
getGameServerOptions
().
MaxAvatarsIn
Team
);
this
.
avatars
=
new
ArrayList
<>(
G
AME_OPTIONS
.
avatarLimits
.
singlePlayer
Team
);
}
public
TeamInfo
(
List
<
Integer
>
avatars
)
{
...
...
@@ -44,7 +44,7 @@ public class TeamInfo {
}
public
boolean
addAvatar
(
Avatar
avatar
)
{
if
(
size
()
>=
Grasscutter
.
getConfig
().
getGameServerOptions
().
MaxAvatarsInTeam
||
contains
(
avatar
))
{
if
(
contains
(
avatar
))
{
return
false
;
}
...
...
@@ -64,7 +64,7 @@ public class TeamInfo {
}
public
void
copyFrom
(
TeamInfo
team
)
{
copyFrom
(
team
,
G
rasscutter
.
getConfig
().
getGameServerOptions
().
MaxAvatarsIn
Team
);
copyFrom
(
team
,
G
AME_OPTIONS
.
avatarLimits
.
singlePlayer
Team
);
}
public
void
copyFrom
(
TeamInfo
team
,
int
maxTeamSize
)
{
...
...
src/main/java/emu/grasscutter/game/player/TeamManager.java
View file @
a8293102
...
...
@@ -5,8 +5,7 @@ import java.util.*;
import
dev.morphia.annotations.Entity
;
import
dev.morphia.annotations.Transient
;
import
emu.grasscutter.GameConstants
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.data.def.AvatarSkillDepotData
;
import
emu.grasscutter.data.excels.AvatarSkillDepotData
;
import
emu.grasscutter.game.avatar.Avatar
;
import
emu.grasscutter.game.entity.EntityAvatar
;
import
emu.grasscutter.game.entity.EntityBaseGadget
;
...
...
@@ -39,6 +38,8 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import
it.unimi.dsi.fastutil.ints.IntOpenHashSet
;
import
it.unimi.dsi.fastutil.ints.IntSet
;
import
static
emu
.
grasscutter
.
Configuration
.*;
@Entity
public
class
TeamManager
{
@Transient
private
Player
player
;
...
...
@@ -103,6 +104,20 @@ public class TeamManager {
this
.
mpTeam
=
mpTeam
;
}
/**
* Search through all teams and if the team matches, return that index.
* Otherwise, return -1.
* No match could mean that the team does not currently belong to the player.
*/
public
int
getTeamId
(
TeamInfo
team
)
{
for
(
int
i
=
1
;
i
<=
this
.
teams
.
size
();
i
++)
{
if
(
this
.
teams
.
get
(
i
).
equals
(
team
))
{
return
i
;
}
}
return
-
1
;
}
public
int
getCurrentTeamId
()
{
// Starts from 1
return
currentTeamIndex
;
...
...
@@ -173,17 +188,119 @@ public class TeamManager {
public
int
getMaxTeamSize
()
{
if
(
getPlayer
().
isInMultiplayer
())
{
int
max
=
G
rasscutter
.
getConfig
().
getGameServerOptions
().
MaxAvatarsInTeamM
ultiplayer
;
int
max
=
G
AME_OPTIONS
.
avatarLimits
.
m
ultiplayer
Team
;
if
(
getPlayer
().
getWorld
().
getHost
()
==
this
.
getPlayer
())
{
return
Math
.
max
(
1
,
(
int
)
Math
.
ceil
(
max
/
(
double
)
getWorld
().
getPlayerCount
()));
}
return
Math
.
max
(
1
,
(
int
)
Math
.
floor
(
max
/
(
double
)
getWorld
().
getPlayerCount
()));
}
return
Grasscutter
.
getConfig
().
getGameServerOptions
().
MaxAvatarsInTeam
;
return
GAME_OPTIONS
.
avatarLimits
.
singlePlayerTeam
;
}
// Methods
/**
* Returns true if there is space to add the number of avatars to the team.
*/
public
boolean
canAddAvatarsToTeam
(
TeamInfo
team
,
int
avatars
)
{
return
team
.
size
()
+
avatars
<=
getMaxTeamSize
();
}
/**
* Returns true if there is space to add to the team.
*/
public
boolean
canAddAvatarToTeam
(
TeamInfo
team
)
{
return
canAddAvatarsToTeam
(
team
,
1
);
}
/**
* Returns true if there is space to add the number of avatars to the current team.
* If the current team is temporary, returns false.
*/
public
boolean
canAddAvatarsToCurrentTeam
(
int
avatars
)
{
if
(
this
.
useTemporarilyTeamIndex
!=
-
1
){
return
false
;
}
return
canAddAvatarsToTeam
(
this
.
getCurrentTeamInfo
(),
avatars
);
}
/**
* Returns true if there is space to add to the current team.
* If the current team is temporary, returns false.
*/
public
boolean
canAddAvatarToCurrentTeam
()
{
return
canAddAvatarsToCurrentTeam
(
1
);
}
/**
* Try to add the collection of avatars to the team.
* Returns true if all were successfully added.
* If some can not be added, returns false and does not add any.
*/
public
boolean
addAvatarsToTeam
(
TeamInfo
team
,
Collection
<
Avatar
>
avatars
)
{
if
(!
canAddAvatarsToTeam
(
team
,
avatars
.
size
()))
{
return
false
;
}
// Convert avatars into a collection of avatar IDs, then add
team
.
getAvatars
().
addAll
(
avatars
.
stream
().
map
(
a
->
a
.
getAvatarId
()).
toList
());
// Update team
if
(
this
.
getPlayer
().
isInMultiplayer
())
{
if
(
team
.
equals
(
this
.
getMpTeam
()))
{
// MP team Packet
this
.
updateTeamEntities
(
new
PacketChangeMpTeamAvatarRsp
(
getPlayer
(),
team
));
}
}
else
{
// SP team update packet
getPlayer
().
sendPacket
(
new
PacketAvatarTeamUpdateNotify
(
getPlayer
()));
int
teamId
=
this
.
getTeamId
(
team
);
if
(
teamId
!=
-
1
)
{
// This is one of the player's teams
// Update entites
if
(
teamId
==
this
.
getCurrentTeamId
())
{
this
.
updateTeamEntities
(
new
PacketSetUpAvatarTeamRsp
(
getPlayer
(),
teamId
,
team
));
}
else
{
getPlayer
().
sendPacket
(
new
PacketSetUpAvatarTeamRsp
(
getPlayer
(),
teamId
,
team
));
}
}
}
return
true
;
}
/**
* Try to add an avatar to a team.
* Returns true if successful.
*/
public
boolean
addAvatarToTeam
(
TeamInfo
team
,
Avatar
avatar
){
return
addAvatarsToTeam
(
team
,
Collections
.
singleton
(
avatar
));
}
/**
* Try to add the collection of avatars to the current team.
* Will not modify a temporary team.
* Returns true if all were successfully added.
* If some can not be added, returns false and does not add any.
*/
public
boolean
addAvatarsToCurrentTeam
(
Collection
<
Avatar
>
avatars
)
{
if
(
this
.
useTemporarilyTeamIndex
!=
-
1
){
return
false
;
}
return
addAvatarsToTeam
(
this
.
getCurrentTeamInfo
(),
avatars
);
}
/**
* Try to add an avatar to the current team.
* Will not modify a temporary team.
* Returns true if successful.
*/
public
boolean
addAvatarToCurrentTeam
(
Avatar
avatar
)
{
return
addAvatarsToCurrentTeam
(
Collections
.
singleton
(
avatar
));
}
private
void
updateTeamResonances
()
{
Int2IntOpenHashMap
map
=
new
Int2IntOpenHashMap
();
...
...
@@ -235,7 +352,7 @@ public class TeamManager {
// Add back entities into team
for
(
int
i
=
0
;
i
<
this
.
getCurrentTeamInfo
().
getAvatars
().
size
();
i
++)
{
int
avatarId
=
this
.
getCurrentTeamInfo
().
getAvatars
().
get
(
i
);
EntityAvatar
entity
=
null
;
EntityAvatar
entity
;
if
(
existingAvatars
.
containsKey
(
avatarId
))
{
entity
=
existingAvatars
.
get
(
avatarId
);
...
...
@@ -302,8 +419,8 @@ public class TeamManager {
// Set team data
LinkedHashSet
<
Avatar
>
newTeam
=
new
LinkedHashSet
<>();
for
(
int
i
=
0
;
i
<
list
.
size
();
i
++
)
{
Avatar
avatar
=
getPlayer
().
getAvatars
().
getAvatarByGuid
(
list
.
get
(
i
)
);
for
(
Long
aLong
:
list
)
{
Avatar
avatar
=
getPlayer
().
getAvatars
().
getAvatarByGuid
(
aLong
);
if
(
avatar
==
null
||
newTeam
.
contains
(
avatar
))
{
// Should never happen
return
;
...
...
@@ -313,19 +430,7 @@ public class TeamManager {
// Clear current team info and add avatars from our new team
teamInfo
.
getAvatars
().
clear
();
for
(
Avatar
avatar
:
newTeam
)
{
teamInfo
.
addAvatar
(
avatar
);
}
// Update packet
getPlayer
().
sendPacket
(
new
PacketAvatarTeamUpdateNotify
(
getPlayer
()));
// Update entites
if
(
teamId
==
this
.
getCurrentTeamId
())
{
this
.
updateTeamEntities
(
new
PacketSetUpAvatarTeamRsp
(
getPlayer
(),
teamId
,
teamInfo
));
}
else
{
getPlayer
().
sendPacket
(
new
PacketSetUpAvatarTeamRsp
(
getPlayer
(),
teamId
,
teamInfo
));
}
this
.
addAvatarsToTeam
(
teamInfo
,
newTeam
);
}
public
void
setupMpTeam
(
List
<
Long
>
list
)
{
...
...
@@ -338,8 +443,8 @@ public class TeamManager {
// Set team data
LinkedHashSet
<
Avatar
>
newTeam
=
new
LinkedHashSet
<>();
for
(
int
i
=
0
;
i
<
list
.
size
();
i
++
)
{
Avatar
avatar
=
getPlayer
().
getAvatars
().
getAvatarByGuid
(
list
.
get
(
i
)
);
for
(
Long
aLong
:
list
)
{
Avatar
avatar
=
getPlayer
().
getAvatars
().
getAvatarByGuid
(
aLong
);
if
(
avatar
==
null
||
newTeam
.
contains
(
avatar
))
{
// Should never happen
return
;
...
...
@@ -349,16 +454,11 @@ public class TeamManager {
// Clear current team info and add avatars from our new team
teamInfo
.
getAvatars
().
clear
();
for
(
Avatar
avatar
:
newTeam
)
{
teamInfo
.
addAvatar
(
avatar
);
}
// Packet
this
.
updateTeamEntities
(
new
PacketChangeMpTeamAvatarRsp
(
getPlayer
(),
teamInfo
));
this
.
addAvatarsToTeam
(
teamInfo
,
newTeam
);
}
public
void
setupTemporaryTeam
(
List
<
List
<
Long
>>
guidList
)
{
var
t
eam
=
guidList
.
stream
().
map
(
list
->
{
this
.
temporaryT
eam
=
guidList
.
stream
().
map
(
list
->
{
// Sanity checks
if
(
list
.
size
()
==
0
||
list
.
size
()
>
getMaxTeamSize
())
{
return
null
;
...
...
@@ -366,8 +466,8 @@ public class TeamManager {
// Set team data
LinkedHashSet
<
Avatar
>
newTeam
=
new
LinkedHashSet
<>();
for
(
int
i
=
0
;
i
<
list
.
size
();
i
++
)
{
Avatar
avatar
=
getPlayer
().
getAvatars
().
getAvatarByGuid
(
list
.
get
(
i
)
);
for
(
Long
aLong
:
list
)
{
Avatar
avatar
=
getPlayer
().
getAvatars
().
getAvatarByGuid
(
aLong
);
if
(
avatar
==
null
||
newTeam
.
contains
(
avatar
))
{
// Should never happen
return
null
;
...
...
@@ -383,7 +483,6 @@ public class TeamManager {
.
filter
(
Objects:
:
nonNull
)
.
map
(
TeamInfo:
:
new
)
.
toList
();
this
.
temporaryTeam
=
team
;
}
public
void
useTemporaryTeam
(
int
index
)
{
...
...
@@ -455,7 +554,7 @@ public class TeamManager {
this
.
setCurrentCharacterIndex
(
index
);
// Old entity motion state
oldEntity
.
setMotionState
(
MotionState
.
MOTION_STANDBY
);
oldEntity
.
setMotionState
(
MotionState
.
MOTION_
STATE_
STANDBY
);
// Remove and Add
getPlayer
().
getScene
().
replaceEntity
(
oldEntity
,
newEntity
);
...
...
@@ -472,7 +571,7 @@ public class TeamManager {
PlayerDieType
dieType
=
deadAvatar
.
getKilledType
();
int
killedBy
=
deadAvatar
.
getKilledBy
();
if
(
dieType
==
PlayerDieType
.
PLAYER_DIE_DRAWN
)
{
if
(
dieType
==
PlayerDieType
.
PLAYER_DIE_
TYPE_
DRAWN
)
{
// Died in water. Do not replace
// The official server has skipped this notify and will just respawn the team immediately after the animation.
// TODO: Perhaps find a way to get vanilla experience?
...
...
@@ -557,7 +656,7 @@ public class TeamManager {
// return;
// }
// }
player
.
get
Movement
Manager
().
resetTim
er
();
// prevent drowning immediately after respawn
player
.
get
Stamina
Manager
().
stopSustainedStaminaHandl
er
();
// prevent drowning immediately after respawn
// Revive all team members
for
(
EntityAvatar
entity
:
getActiveTeam
())
{
...
...
@@ -570,7 +669,7 @@ public class TeamManager {
}
// Teleport player
getPlayer
().
sendPacket
(
new
PacketPlayerEnterSceneNotify
(
getPlayer
(),
EnterType
.
ENTER_SELF
,
EnterReason
.
Revival
,
3
,
GameConstants
.
START_POSITION
));
getPlayer
().
sendPacket
(
new
PacketPlayerEnterSceneNotify
(
getPlayer
(),
EnterType
.
ENTER_
TYPE_
SELF
,
EnterReason
.
Revival
,
3
,
GameConstants
.
START_POSITION
));
// Set player position
player
.
setSceneId
(
3
);
...
...
src/main/java/emu/grasscutter/game/props/ElementType.java
View file @
a8293102
...
...
@@ -9,21 +9,22 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import
it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
;
public
enum
ElementType
{
None
(
0
,
FightProperty
.
FIGHT_PROP_MAX_FIRE_ENERGY
),
Fire
(
1
,
FightProperty
.
FIGHT_PROP_MAX_FIRE_ENERGY
,
10101
,
"TeamResonance_Fire_Lv2"
),
Water
(
2
,
FightProperty
.
FIGHT_PROP_MAX_WATER_ENERGY
,
10201
,
"TeamResonance_Water_Lv2"
),
Grass
(
3
,
FightProperty
.
FIGHT_PROP_MAX_GRASS_ENERGY
),
Electric
(
4
,
FightProperty
.
FIGHT_PROP_MAX_ELEC_ENERGY
,
10401
,
"TeamResonance_Electric_Lv2"
),
Ice
(
5
,
FightProperty
.
FIGHT_PROP_MAX_ICE_ENERGY
,
10601
,
"TeamResonance_Ice_Lv2"
),
Frozen
(
6
,
FightProperty
.
FIGHT_PROP_MAX_ICE_ENERGY
),
Wind
(
7
,
FightProperty
.
FIGHT_PROP_MAX_WIND_ENERGY
,
10301
,
"TeamResonance_Wind_Lv2"
),
Rock
(
8
,
FightProperty
.
FIGHT_PROP_MAX_ROCK_ENERGY
,
10701
,
"TeamResonance_Rock_Lv2"
),
AntiFire
(
9
,
FightProperty
.
FIGHT_PROP_MAX_FIRE_ENERGY
),
Default
(
255
,
FightProperty
.
FIGHT_PROP_MAX_FIRE_ENERGY
,
10801
,
"TeamResonance_AllDifferent"
);
None
(
0
,
FightProperty
.
FIGHT_PROP_CUR_FIRE_ENERGY
,
FightProperty
.
FIGHT_PROP_MAX_FIRE_ENERGY
),
Fire
(
1
,
FightProperty
.
FIGHT_PROP_CUR_FIRE_ENERGY
,
FightProperty
.
FIGHT_PROP_MAX_FIRE_ENERGY
,
10101
,
"TeamResonance_Fire_Lv2"
),
Water
(
2
,
FightProperty
.
FIGHT_PROP_CUR_WATER_ENERGY
,
FightProperty
.
FIGHT_PROP_MAX_WATER_ENERGY
,
10201
,
"TeamResonance_Water_Lv2"
),
Grass
(
3
,
FightProperty
.
FIGHT_PROP_CUR_GRASS_ENERGY
,
FightProperty
.
FIGHT_PROP_MAX_GRASS_ENERGY
),
Electric
(
4
,
FightProperty
.
FIGHT_PROP_CUR_ELEC_ENERGY
,
FightProperty
.
FIGHT_PROP_MAX_ELEC_ENERGY
,
10401
,
"TeamResonance_Electric_Lv2"
),
Ice
(
5
,
FightProperty
.
FIGHT_PROP_CUR_ICE_ENERGY
,
FightProperty
.
FIGHT_PROP_MAX_ICE_ENERGY
,
10601
,
"TeamResonance_Ice_Lv2"
),
Frozen
(
6
,
FightProperty
.
FIGHT_PROP_CUR_ICE_ENERGY
,
FightProperty
.
FIGHT_PROP_MAX_ICE_ENERGY
),
Wind
(
7
,
FightProperty
.
FIGHT_PROP_CUR_WIND_ENERGY
,
FightProperty
.
FIGHT_PROP_MAX_WIND_ENERGY
,
10301
,
"TeamResonance_Wind_Lv2"
),
Rock
(
8
,
FightProperty
.
FIGHT_PROP_CUR_ROCK_ENERGY
,
FightProperty
.
FIGHT_PROP_MAX_ROCK_ENERGY
,
10701
,
"TeamResonance_Rock_Lv2"
),
AntiFire
(
9
,
FightProperty
.
FIGHT_PROP_CUR_FIRE_ENERGY
,
FightProperty
.
FIGHT_PROP_MAX_FIRE_ENERGY
),
Default
(
255
,
FightProperty
.
FIGHT_PROP_CUR_FIRE_ENERGY
,
FightProperty
.
FIGHT_PROP_MAX_FIRE_ENERGY
,
10801
,
"TeamResonance_AllDifferent"
);
private
final
int
value
;
private
final
int
teamResonanceId
;
private
final
FightProperty
energyProperty
;
private
final
FightProperty
curEnergyProp
;
private
final
FightProperty
maxEnergyProp
;
private
final
int
configHash
;
private
static
final
Int2ObjectMap
<
ElementType
>
map
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Map
<
String
,
ElementType
>
stringMap
=
new
HashMap
<>();
...
...
@@ -35,13 +36,14 @@ public enum ElementType {
});
}
private
ElementType
(
int
value
,
FightProperty
e
nergyProp
erty
)
{
this
(
value
,
e
nergyProp
erty
,
0
,
null
);
private
ElementType
(
int
value
,
FightProperty
curE
nergyProp
,
FightProperty
maxEnergyProp
)
{
this
(
value
,
curE
nergyProp
,
maxEnergyProp
,
0
,
null
);
}
private
ElementType
(
int
value
,
FightProperty
e
nergyProp
erty
,
int
teamResonanceId
,
String
configName
)
{
private
ElementType
(
int
value
,
FightProperty
curE
nergyProp
,
FightProperty
maxEnergyProp
,
int
teamResonanceId
,
String
configName
)
{
this
.
value
=
value
;
this
.
energyProperty
=
energyProperty
;
this
.
curEnergyProp
=
curEnergyProp
;
this
.
maxEnergyProp
=
maxEnergyProp
;
this
.
teamResonanceId
=
teamResonanceId
;
if
(
configName
!=
null
)
{
this
.
configHash
=
Utils
.
abilityHash
(
configName
);
...
...
@@ -54,8 +56,12 @@ public enum ElementType {
return
value
;
}
public
FightProperty
getEnergyProperty
()
{
return
energyProperty
;
public
FightProperty
getCurEnergyProp
()
{
return
curEnergyProp
;
}
public
FightProperty
getMaxEnergyProp
()
{
return
maxEnergyProp
;
}
public
int
getTeamResonanceId
()
{
...
...
src/main/java/emu/grasscutter/game/props/MonsterType.java
0 → 100644
View file @
a8293102
package
emu.grasscutter.game.props
;
import
java.util.HashMap
;
import
java.util.Map
;
import
java.util.stream.Stream
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectMap
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
;
public
enum
MonsterType
{
MONSTER_NONE
(
0
),
MONSTER_ORDINARY
(
1
),
MONSTER_BOSS
(
2
),
MONSTER_ENV_ANIMAL
(
3
),
MONSTER_LITTLE_MONSTER
(
4
),
MONSTER_FISH
(
5
);
private
final
int
value
;
private
static
final
Int2ObjectMap
<
MonsterType
>
map
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Map
<
String
,
MonsterType
>
stringMap
=
new
HashMap
<>();
static
{
Stream
.
of
(
values
()).
forEach
(
e
->
{
map
.
put
(
e
.
getValue
(),
e
);
stringMap
.
put
(
e
.
name
(),
e
);
});
}
private
MonsterType
(
int
value
)
{
this
.
value
=
value
;
}
public
int
getValue
()
{
return
value
;
}
public
static
MonsterType
getTypeByValue
(
int
value
)
{
return
map
.
getOrDefault
(
value
,
MONSTER_NONE
);
}
public
static
MonsterType
getTypeByName
(
String
name
)
{
return
stringMap
.
getOrDefault
(
name
,
MONSTER_NONE
);
}
}
src/main/java/emu/grasscutter/game/props/OpenState.java
View file @
a8293102
...
...
@@ -94,6 +94,7 @@ public enum OpenState {
OPEN_STATE_GUIDE_RELICRESOLVE
(
84
),
OPEN_STATE_GUIDE_GGUIDE
(
85
),
OPEN_STATE_GUIDE_GGUIDE_HINT
(
86
),
OPEN_STATE_GUIDE_RIGHT_TEAM
(
90
),
// mobile phone only!
OPEN_STATE_CITY_REPUATION_MENGDE
(
800
),
OPEN_STATE_CITY_REPUATION_LIYUE
(
801
),
OPEN_STATE_CITY_REPUATION_UI_HINT
(
802
),
...
...
src/main/java/emu/grasscutter/game/props/PlayerProperty.java
View file @
a8293102
...
...
@@ -30,7 +30,7 @@ public enum PlayerProperty {
// his gems and then got a money refund, so negative is allowed.
PROP_PLAYER_SCOIN
(
10016
),
// Mora [0, +inf)
PROP_PLAYER_MP_SETTING_TYPE
(
10017
),
// Do you allow other players to join your game? [0=no 1=direct 2=approval]
PROP_IS_MP_MODE_AVAILABLE
(
10018
),
//
Are you not
in
a
quest or something that disables MP
?
[0, 1]
PROP_IS_MP_MODE_AVAILABLE
(
10018
),
//
0 if
in quest or something that disables MP [0, 1]
PROP_PLAYER_WORLD_LEVEL
(
10019
),
// [0, 8]
PROP_PLAYER_RESIN
(
10020
),
// Original Resin [0, +inf)
PROP_PLAYER_WAIT_SUB_HCOIN
(
10022
),
...
...
src/main/java/emu/grasscutter/game/props/WeaponType.java
0 → 100644
View file @
a8293102
package
emu.grasscutter.game.props
;
import
java.util.HashMap
;
import
java.util.Map
;
import
java.util.stream.Stream
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectMap
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
;
public
enum
WeaponType
{
WEAPON_NONE
(
0
),
WEAPON_SWORD_ONE_HAND
(
1
,
10
,
5
),
WEAPON_CROSSBOW
(
2
),
WEAPON_STAFF
(
3
),
WEAPON_DOUBLE_DAGGER
(
4
),
WEAPON_KATANA
(
5
),
WEAPON_SHURIKEN
(
6
),
WEAPON_STICK
(
7
),
WEAPON_SPEAR
(
8
),
WEAPON_SHIELD_SMALL
(
9
),
WEAPON_CATALYST
(
10
,
0
,
10
),
WEAPON_CLAYMORE
(
11
,
0
,
10
),
WEAPON_BOW
(
12
,
0
,
5
),
WEAPON_POLE
(
13
,
0
,
4
);
private
final
int
value
;
private
int
energyGainInitialProbability
;
private
int
energyGainIncreaseProbability
;
private
static
final
Int2ObjectMap
<
WeaponType
>
map
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Map
<
String
,
WeaponType
>
stringMap
=
new
HashMap
<>();
static
{
Stream
.
of
(
values
()).
forEach
(
e
->
{
map
.
put
(
e
.
getValue
(),
e
);
stringMap
.
put
(
e
.
name
(),
e
);
});
}
private
WeaponType
(
int
value
)
{
this
.
value
=
value
;
}
private
WeaponType
(
int
value
,
int
energyGainInitialProbability
,
int
energyGainIncreaseProbability
)
{
this
.
value
=
value
;
this
.
energyGainInitialProbability
=
energyGainInitialProbability
;
this
.
energyGainIncreaseProbability
=
energyGainIncreaseProbability
;
}
public
int
getValue
()
{
return
value
;
}
public
int
getEnergyGainInitialProbability
()
{
return
energyGainInitialProbability
;
}
public
int
getEnergyGainIncreaseProbability
()
{
return
energyGainIncreaseProbability
;
}
public
static
WeaponType
getTypeByValue
(
int
value
)
{
return
map
.
getOrDefault
(
value
,
WEAPON_NONE
);
}
public
static
WeaponType
getTypeByName
(
String
name
)
{
return
stringMap
.
getOrDefault
(
name
,
WEAPON_NONE
);
}
}
src/main/java/emu/grasscutter/game/quest/GameMainQuest.java
0 → 100644
View file @
a8293102
package
emu.grasscutter.game.quest
;
import
java.util.HashMap
;
import
java.util.Map
;
import
emu.grasscutter.server.packet.send.PacketCodexDataUpdateNotify
;
import
org.bson.types.ObjectId
;
import
dev.morphia.annotations.Entity
;
import
dev.morphia.annotations.Id
;
import
dev.morphia.annotations.Indexed
;
import
dev.morphia.annotations.Transient
;
import
emu.grasscutter.data.GameData
;
import
emu.grasscutter.data.binout.MainQuestData
;
import
emu.grasscutter.data.excels.RewardData
;
import
emu.grasscutter.database.DatabaseHelper
;
import
emu.grasscutter.game.player.Player
;
import
emu.grasscutter.game.props.ActionReason
;
import
emu.grasscutter.game.quest.enums.ParentQuestState
;
import
emu.grasscutter.game.quest.enums.QuestState
;
import
emu.grasscutter.net.proto.ChildQuestOuterClass.ChildQuest
;
import
emu.grasscutter.net.proto.ParentQuestOuterClass.ParentQuest
;
import
emu.grasscutter.net.proto.QuestOuterClass.Quest
;
import
emu.grasscutter.server.packet.send.PacketFinishedParentQuestUpdateNotify
;
import
emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify
;
import
emu.grasscutter.server.packet.send.PacketQuestProgressUpdateNotify
;
import
emu.grasscutter.utils.Utils
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectMap
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
;
@Entity
(
value
=
"quests"
,
useDiscriminator
=
false
)
public
class
GameMainQuest
{
@Id
private
ObjectId
id
;
@Indexed
private
int
ownerUid
;
@Transient
private
Player
owner
;
private
Map
<
Integer
,
GameQuest
>
childQuests
;
private
int
parentQuestId
;
private
int
[]
questVars
;
private
ParentQuestState
state
;
private
boolean
isFinished
;
@Deprecated
// Morphia only. Do not use.
public
GameMainQuest
()
{}
public
GameMainQuest
(
Player
player
,
int
parentQuestId
)
{
this
.
owner
=
player
;
this
.
ownerUid
=
player
.
getUid
();
this
.
parentQuestId
=
parentQuestId
;
this
.
childQuests
=
new
HashMap
<>();
this
.
questVars
=
new
int
[
5
];
this
.
state
=
ParentQuestState
.
PARENT_QUEST_STATE_NONE
;
}
public
int
getParentQuestId
()
{
return
parentQuestId
;
}
public
int
getOwnerUid
()
{
return
ownerUid
;
}
public
Player
getOwner
()
{
return
owner
;
}
public
void
setOwner
(
Player
player
)
{
if
(
player
.
getUid
()
!=
this
.
getOwnerUid
())
return
;
this
.
owner
=
player
;
}
public
Map
<
Integer
,
GameQuest
>
getChildQuests
()
{
return
childQuests
;
}
public
GameQuest
getChildQuestById
(
int
id
)
{
return
this
.
getChildQuests
().
get
(
id
);
}
public
int
[]
getQuestVars
()
{
return
questVars
;
}
public
ParentQuestState
getState
()
{
return
state
;
}
public
boolean
isFinished
()
{
return
isFinished
;
}
public
void
finish
()
{
this
.
isFinished
=
true
;
this
.
state
=
ParentQuestState
.
PARENT_QUEST_STATE_FINISHED
;
this
.
getOwner
().
getSession
().
send
(
new
PacketFinishedParentQuestUpdateNotify
(
this
));
this
.
getOwner
().
getSession
().
send
(
new
PacketCodexDataUpdateNotify
(
this
));
this
.
save
();
// Add rewards
MainQuestData
mainQuestData
=
GameData
.
getMainQuestDataMap
().
get
(
this
.
getParentQuestId
());
for
(
int
rewardId
:
mainQuestData
.
getRewardIdList
())
{
RewardData
rewardData
=
GameData
.
getRewardDataMap
().
get
(
rewardId
);
if
(
rewardData
==
null
)
{
continue
;
}
getOwner
().
getInventory
().
addItemParamDatas
(
rewardData
.
getRewardItemList
(),
ActionReason
.
QuestReward
);
}
}
public
void
save
()
{
DatabaseHelper
.
saveQuest
(
this
);
}
public
ParentQuest
toProto
()
{
ParentQuest
.
Builder
proto
=
ParentQuest
.
newBuilder
()
.
setParentQuestId
(
getParentQuestId
())
.
setIsFinished
(
isFinished
())
.
setParentQuestState
(
getState
().
getValue
());
for
(
GameQuest
quest
:
this
.
getChildQuests
().
values
())
{
ChildQuest
childQuest
=
ChildQuest
.
newBuilder
()
.
setQuestId
(
quest
.
getQuestId
())
.
setState
(
quest
.
getState
().
getValue
())
.
build
();
proto
.
addChildQuestList
(
childQuest
);
}
if
(
getQuestVars
()
!=
null
)
{
for
(
int
i
:
getQuestVars
())
{
proto
.
addQuestVar
(
i
);
}
}
return
proto
.
build
();
}
}
src/main/java/emu/grasscutter/game/quest/GameQuest.java
0 → 100644
View file @
a8293102
package
emu.grasscutter.game.quest
;
import
dev.morphia.annotations.Entity
;
import
dev.morphia.annotations.Transient
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.data.GameData
;
import
emu.grasscutter.data.binout.MainQuestData
;
import
emu.grasscutter.data.binout.MainQuestData.SubQuestData
;
import
emu.grasscutter.data.excels.QuestData
;
import
emu.grasscutter.data.excels.QuestData.QuestCondition
;
import
emu.grasscutter.game.player.Player
;
import
emu.grasscutter.game.quest.enums.LogicType
;
import
emu.grasscutter.game.quest.enums.QuestState
;
import
emu.grasscutter.net.proto.QuestOuterClass.Quest
;
import
emu.grasscutter.server.packet.send.PacketCodexDataUpdateNotify
;
import
emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify
;
import
emu.grasscutter.server.packet.send.PacketQuestProgressUpdateNotify
;
import
emu.grasscutter.utils.Utils
;
@Entity
public
class
GameQuest
{
@Transient
private
GameMainQuest
mainQuest
;
@Transient
private
QuestData
questData
;
private
int
questId
;
private
int
mainQuestId
;
private
QuestState
state
;
private
int
startTime
;
private
int
acceptTime
;
private
int
finishTime
;
private
int
[]
finishProgressList
;
private
int
[]
failProgressList
;
@Deprecated
// Morphia only. Do not use.
public
GameQuest
()
{}
public
GameQuest
(
GameMainQuest
mainQuest
,
QuestData
questData
)
{
this
.
mainQuest
=
mainQuest
;
this
.
questId
=
questData
.
getId
();
this
.
mainQuestId
=
questData
.
getMainId
();
this
.
questData
=
questData
;
this
.
acceptTime
=
Utils
.
getCurrentSeconds
();
this
.
startTime
=
this
.
acceptTime
;
this
.
state
=
QuestState
.
QUEST_STATE_UNFINISHED
;
if
(
questData
.
getFinishCond
()
!=
null
&&
questData
.
getAcceptCond
().
length
!=
0
)
{
this
.
finishProgressList
=
new
int
[
questData
.
getFinishCond
().
length
];
}
if
(
questData
.
getFailCond
()
!=
null
&&
questData
.
getFailCond
().
length
!=
0
)
{
this
.
failProgressList
=
new
int
[
questData
.
getFailCond
().
length
];
}
this
.
mainQuest
.
getChildQuests
().
put
(
this
.
questId
,
this
);
}
public
GameMainQuest
getMainQuest
()
{
return
mainQuest
;
}
public
void
setMainQuest
(
GameMainQuest
mainQuest
)
{
this
.
mainQuest
=
mainQuest
;
}
public
Player
getOwner
()
{
return
getMainQuest
().
getOwner
();
}
public
int
getQuestId
()
{
return
questId
;
}
public
int
getMainQuestId
()
{
return
mainQuestId
;
}
public
QuestData
getData
()
{
return
questData
;
}
public
void
setConfig
(
QuestData
config
)
{
if
(
this
.
getQuestId
()
!=
config
.
getId
())
return
;
this
.
questData
=
config
;
}
public
QuestState
getState
()
{
return
state
;
}
public
void
setState
(
QuestState
state
)
{
this
.
state
=
state
;
}
public
int
getStartTime
()
{
return
startTime
;
}
public
void
setStartTime
(
int
startTime
)
{
this
.
startTime
=
startTime
;
}
public
int
getAcceptTime
()
{
return
acceptTime
;
}
public
void
setAcceptTime
(
int
acceptTime
)
{
this
.
acceptTime
=
acceptTime
;
}
public
int
getFinishTime
()
{
return
finishTime
;
}
public
void
setFinishTime
(
int
finishTime
)
{
this
.
finishTime
=
finishTime
;
}
public
int
[]
getFinishProgressList
()
{
return
finishProgressList
;
}
public
void
setFinishProgress
(
int
index
,
int
value
)
{
finishProgressList
[
index
]
=
value
;
}
public
int
[]
getFailProgressList
()
{
return
failProgressList
;
}
public
void
setFailProgress
(
int
index
,
int
value
)
{
failProgressList
[
index
]
=
value
;
}
public
void
finish
()
{
this
.
state
=
QuestState
.
QUEST_STATE_FINISHED
;
this
.
finishTime
=
Utils
.
getCurrentSeconds
();
if
(
this
.
getFinishProgressList
()
!=
null
)
{
for
(
int
i
=
0
;
i
<
getFinishProgressList
().
length
;
i
++)
{
getFinishProgressList
()[
i
]
=
1
;
}
}
this
.
getOwner
().
getSession
().
send
(
new
PacketQuestProgressUpdateNotify
(
this
));
this
.
getOwner
().
getSession
().
send
(
new
PacketQuestListUpdateNotify
(
this
));
if
(
this
.
getData
().
finishParent
())
{
// This quest finishes the questline - the main quest will also save the quest to db so we dont have to call save() here
this
.
getMainQuest
().
finish
();
}
else
{
// Try and accept other quests if possible
this
.
tryAcceptQuestLine
();
this
.
save
();
}
}
public
boolean
tryAcceptQuestLine
()
{
try
{
MainQuestData
questConfig
=
GameData
.
getMainQuestDataMap
().
get
(
this
.
getMainQuestId
());
for
(
SubQuestData
subQuest
:
questConfig
.
getSubQuests
())
{
GameQuest
quest
=
getMainQuest
().
getChildQuestById
(
subQuest
.
getSubId
());
if
(
quest
==
null
)
{
QuestData
questData
=
GameData
.
getQuestDataMap
().
get
(
subQuest
.
getSubId
());
if
(
questData
==
null
||
questData
.
getAcceptCond
()
==
null
||
questData
.
getAcceptCond
().
length
==
0
)
{
continue
;
}
int
[]
accept
=
new
int
[
questData
.
getAcceptCond
().
length
];
// TODO
for
(
int
i
=
0
;
i
<
questData
.
getAcceptCond
().
length
;
i
++)
{
QuestCondition
condition
=
questData
.
getAcceptCond
()[
i
];
boolean
result
=
getOwner
().
getServer
().
getQuestHandler
().
triggerCondition
(
this
,
condition
,
condition
.
getParam
());
accept
[
i
]
=
result
?
1
:
0
;
}
boolean
shouldAccept
=
LogicType
.
calculate
(
questData
.
getAcceptCondComb
(),
accept
);
if
(
shouldAccept
)
{
this
.
getOwner
().
getQuestManager
().
addQuest
(
questData
.
getId
());
}
}
}
}
catch
(
Exception
e
)
{
Grasscutter
.
getLogger
().
error
(
"An error occurred while trying to accept quest."
,
e
);
}
return
false
;
}
public
void
save
()
{
getMainQuest
().
save
();
}
public
Quest
toProto
()
{
Quest
.
Builder
proto
=
Quest
.
newBuilder
()
.
setQuestId
(
this
.
getQuestId
())
.
setState
(
this
.
getState
().
getValue
())
.
setParentQuestId
(
this
.
getMainQuestId
())
.
setStartTime
(
this
.
getStartTime
())
.
setStartGameTime
(
438
)
.
setAcceptTime
(
this
.
getAcceptTime
());
if
(
this
.
getFinishProgressList
()
!=
null
)
{
for
(
int
i
:
this
.
getFinishProgressList
())
{
proto
.
addFinishProgressList
(
i
);
}
}
if
(
this
.
getFailProgressList
()
!=
null
)
{
for
(
int
i
:
this
.
getFailProgressList
())
{
proto
.
addFailProgressList
(
i
);
}
}
return
proto
.
build
();
}
}
src/main/java/emu/grasscutter/game/quest/QuestManager.java
0 → 100644
View file @
a8293102
package
emu.grasscutter.game.quest
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.Set
;
import
java.util.function.Consumer
;
import
emu.grasscutter.data.GameData
;
import
emu.grasscutter.data.excels.QuestData
;
import
emu.grasscutter.data.excels.QuestData.QuestCondition
;
import
emu.grasscutter.database.DatabaseHelper
;
import
emu.grasscutter.game.player.Player
;
import
emu.grasscutter.game.quest.enums.QuestTrigger
;
import
emu.grasscutter.game.quest.enums.LogicType
;
import
emu.grasscutter.game.quest.enums.QuestState
;
import
emu.grasscutter.server.packet.send.PacketFinishedParentQuestUpdateNotify
;
import
emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify
;
import
emu.grasscutter.server.packet.send.PacketQuestProgressUpdateNotify
;
import
emu.grasscutter.server.packet.send.PacketServerCondMeetQuestListUpdateNotify
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectMap
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
;
public
class
QuestManager
{
private
final
Player
player
;
private
final
Int2ObjectMap
<
GameMainQuest
>
quests
;
public
QuestManager
(
Player
player
)
{
this
.
player
=
player
;
this
.
quests
=
new
Int2ObjectOpenHashMap
<>();
}
public
Player
getPlayer
()
{
return
player
;
}
public
Int2ObjectMap
<
GameMainQuest
>
getQuests
()
{
return
quests
;
}
public
GameMainQuest
getMainQuestById
(
int
mainQuestId
)
{
return
getQuests
().
get
(
mainQuestId
);
}
public
GameQuest
getQuestById
(
int
questId
)
{
QuestData
questConfig
=
GameData
.
getQuestDataMap
().
get
(
questId
);
if
(
questConfig
==
null
)
{
return
null
;
}
GameMainQuest
mainQuest
=
getQuests
().
get
(
questConfig
.
getMainId
());
if
(
mainQuest
==
null
)
{
return
null
;
}
return
mainQuest
.
getChildQuests
().
get
(
questId
);
}
public
void
forEachQuest
(
Consumer
<
GameQuest
>
callback
)
{
for
(
GameMainQuest
mainQuest
:
getQuests
().
values
())
{
for
(
GameQuest
quest
:
mainQuest
.
getChildQuests
().
values
())
{
callback
.
accept
(
quest
);
}
}
}
public
void
forEachMainQuest
(
Consumer
<
GameMainQuest
>
callback
)
{
for
(
GameMainQuest
mainQuest
:
getQuests
().
values
())
{
callback
.
accept
(
mainQuest
);
}
}
// TODO
public
void
forEachActiveQuest
(
Consumer
<
GameQuest
>
callback
)
{
for
(
GameMainQuest
mainQuest
:
getQuests
().
values
())
{
for
(
GameQuest
quest
:
mainQuest
.
getChildQuests
().
values
())
{
if
(
quest
.
getState
()
!=
QuestState
.
QUEST_STATE_FINISHED
)
{
callback
.
accept
(
quest
);
}
}
}
}
public
GameMainQuest
addMainQuest
(
QuestData
questConfig
)
{
GameMainQuest
mainQuest
=
new
GameMainQuest
(
getPlayer
(),
questConfig
.
getMainId
());
getQuests
().
put
(
mainQuest
.
getParentQuestId
(),
mainQuest
);
getPlayer
().
sendPacket
(
new
PacketFinishedParentQuestUpdateNotify
(
mainQuest
));
return
mainQuest
;
}
public
GameQuest
addQuest
(
int
questId
)
{
QuestData
questConfig
=
GameData
.
getQuestDataMap
().
get
(
questId
);
if
(
questConfig
==
null
)
{
return
null
;
}
// Main quest
GameMainQuest
mainQuest
=
this
.
getMainQuestById
(
questConfig
.
getMainId
());
// Create main quest if it doesnt exist
if
(
mainQuest
==
null
)
{
mainQuest
=
addMainQuest
(
questConfig
);
}
// Sub quest
GameQuest
quest
=
mainQuest
.
getChildQuestById
(
questId
);
if
(
quest
!=
null
)
{
return
null
;
}
// Create
quest
=
new
GameQuest
(
mainQuest
,
questConfig
);
// Save main quest
mainQuest
.
save
();
// Send packet
getPlayer
().
sendPacket
(
new
PacketQuestListUpdateNotify
(
quest
));
return
quest
;
}
public
void
triggerEvent
(
QuestTrigger
condType
,
int
...
params
)
{
Set
<
GameQuest
>
changedQuests
=
new
HashSet
<>();
this
.
forEachActiveQuest
(
quest
->
{
QuestData
data
=
quest
.
getData
();
for
(
int
i
=
0
;
i
<
data
.
getFinishCond
().
length
;
i
++)
{
if
(
quest
.
getFinishProgressList
()
==
null
||
quest
.
getFinishProgressList
().
length
==
0
||
quest
.
getFinishProgressList
()[
i
]
==
1
)
{
continue
;
}
QuestCondition
condition
=
data
.
getFinishCond
()[
i
];
if
(
condition
.
getType
()
!=
condType
)
{
continue
;
}
boolean
result
=
getPlayer
().
getServer
().
getQuestHandler
().
triggerContent
(
quest
,
condition
,
params
);
if
(
result
)
{
quest
.
getFinishProgressList
()[
i
]
=
1
;
changedQuests
.
add
(
quest
);
}
}
});
for
(
GameQuest
quest
:
changedQuests
)
{
LogicType
logicType
=
quest
.
getData
().
getFailCondComb
();
int
[]
progress
=
quest
.
getFinishProgressList
();
// Handle logical comb
boolean
finish
=
LogicType
.
calculate
(
logicType
,
progress
);
// Finish
if
(
finish
)
{
quest
.
finish
();
}
else
{
getPlayer
().
sendPacket
(
new
PacketQuestProgressUpdateNotify
(
quest
));
quest
.
save
();
}
}
}
public
void
loadFromDatabase
()
{
List
<
GameMainQuest
>
quests
=
DatabaseHelper
.
getAllQuests
(
getPlayer
());
for
(
GameMainQuest
mainQuest
:
quests
)
{
mainQuest
.
setOwner
(
this
.
getPlayer
());
for
(
GameQuest
quest
:
mainQuest
.
getChildQuests
().
values
())
{
quest
.
setMainQuest
(
mainQuest
);
quest
.
setConfig
(
GameData
.
getQuestDataMap
().
get
(
quest
.
getQuestId
()));
}
this
.
getQuests
().
put
(
mainQuest
.
getParentQuestId
(),
mainQuest
);
}
}
}
src/main/java/emu/grasscutter/game/quest/QuestValue.java
0 → 100644
View file @
a8293102
package
emu.grasscutter.game.quest
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
emu.grasscutter.game.quest.enums.QuestTrigger
;
@Retention
(
RetentionPolicy
.
RUNTIME
)
public
@interface
QuestValue
{
QuestTrigger
value
();
}
src/main/java/emu/grasscutter/game/quest/ServerQuestHandler.java
0 → 100644
View file @
a8293102
package
emu.grasscutter.game.quest
;
import
java.util.Set
;
import
org.reflections.Reflections
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.data.excels.QuestData.QuestCondition
;
import
emu.grasscutter.game.quest.handlers.QuestBaseHandler
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectMap
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
;
@SuppressWarnings
(
"unchecked"
)
public
class
ServerQuestHandler
{
private
final
Int2ObjectMap
<
QuestBaseHandler
>
condHandlers
;
private
final
Int2ObjectMap
<
QuestBaseHandler
>
contHandlers
;
private
final
Int2ObjectMap
<
QuestBaseHandler
>
execHandlers
;
public
ServerQuestHandler
()
{
this
.
condHandlers
=
new
Int2ObjectOpenHashMap
<>();
this
.
contHandlers
=
new
Int2ObjectOpenHashMap
<>();
this
.
execHandlers
=
new
Int2ObjectOpenHashMap
<>();
this
.
registerHandlers
();
}
public
void
registerHandlers
()
{
this
.
registerHandlers
(
this
.
condHandlers
,
"emu.grasscutter.game.quest.conditions"
);
this
.
registerHandlers
(
this
.
contHandlers
,
"emu.grasscutter.game.quest.content"
);
this
.
registerHandlers
(
this
.
execHandlers
,
"emu.grasscutter.game.quest.exec"
);
}
public
void
registerHandlers
(
Int2ObjectMap
<
QuestBaseHandler
>
map
,
String
packageName
)
{
Reflections
reflections
=
new
Reflections
(
packageName
);
Set
<?>
handlerClasses
=
reflections
.
getSubTypesOf
(
QuestBaseHandler
.
class
);
for
(
Object
obj
:
handlerClasses
)
{
this
.
registerPacketHandler
(
map
,
(
Class
<?
extends
QuestBaseHandler
>)
obj
);
}
}
public
void
registerPacketHandler
(
Int2ObjectMap
<
QuestBaseHandler
>
map
,
Class
<?
extends
QuestBaseHandler
>
handlerClass
)
{
try
{
QuestValue
opcode
=
handlerClass
.
getAnnotation
(
QuestValue
.
class
);
if
(
opcode
==
null
||
opcode
.
value
().
getValue
()
<=
0
)
{
return
;
}
QuestBaseHandler
packetHandler
=
(
QuestBaseHandler
)
handlerClass
.
newInstance
();
map
.
put
(
opcode
.
value
().
getValue
(),
packetHandler
);
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
// TODO make cleaner
public
boolean
triggerCondition
(
GameQuest
quest
,
QuestCondition
condition
,
int
...
params
)
{
QuestBaseHandler
handler
=
condHandlers
.
get
(
condition
.
getType
().
getValue
());
if
(
handler
==
null
||
quest
.
getData
()
==
null
)
{
return
false
;
}
return
handler
.
execute
(
quest
,
condition
,
params
);
}
public
boolean
triggerContent
(
GameQuest
quest
,
QuestCondition
condition
,
int
...
params
)
{
QuestBaseHandler
handler
=
contHandlers
.
get
(
condition
.
getType
().
getValue
());
if
(
handler
==
null
||
quest
.
getData
()
==
null
)
{
return
false
;
}
return
handler
.
execute
(
quest
,
condition
,
params
);
}
public
boolean
triggerExec
(
GameQuest
quest
,
QuestCondition
condition
,
int
...
params
)
{
QuestBaseHandler
handler
=
execHandlers
.
get
(
condition
.
getType
().
getValue
());
if
(
handler
==
null
||
quest
.
getData
()
==
null
)
{
return
false
;
}
return
handler
.
execute
(
quest
,
condition
,
params
);
}
}
src/main/java/emu/grasscutter/game/quest/conditions/BaseCondition.java
0 → 100644
View file @
a8293102
package
emu.grasscutter.game.quest.conditions
;
import
emu.grasscutter.game.quest.QuestValue
;
import
emu.grasscutter.data.excels.QuestData.QuestCondition
;
import
emu.grasscutter.game.quest.GameQuest
;
import
emu.grasscutter.game.quest.enums.QuestTrigger
;
import
emu.grasscutter.game.quest.handlers.QuestBaseHandler
;
@QuestValue
(
QuestTrigger
.
QUEST_CONTENT_NONE
)
public
class
BaseCondition
extends
QuestBaseHandler
{
@Override
public
boolean
execute
(
GameQuest
quest
,
QuestCondition
condition
,
int
...
params
)
{
// TODO Auto-generated method stub
return
false
;
}
}
src/main/java/emu/grasscutter/game/quest/conditions/ConditionPlayerLevelEqualGreater.java
0 → 100644
View file @
a8293102
package
emu.grasscutter.game.quest.conditions
;
import
emu.grasscutter.game.quest.QuestValue
;
import
emu.grasscutter.data.excels.QuestData.QuestCondition
;
import
emu.grasscutter.game.quest.GameQuest
;
import
emu.grasscutter.game.quest.enums.QuestTrigger
;
import
emu.grasscutter.game.quest.handlers.QuestBaseHandler
;
@QuestValue
(
QuestTrigger
.
QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER
)
public
class
ConditionPlayerLevelEqualGreater
extends
QuestBaseHandler
{
@Override
public
boolean
execute
(
GameQuest
quest
,
QuestCondition
condition
,
int
...
params
)
{
return
quest
.
getOwner
().
getLevel
()
>=
params
[
0
];
}
}
src/main/java/emu/grasscutter/game/quest/conditions/ConditionStateEqual.java
0 → 100644
View file @
a8293102
package
emu.grasscutter.game.quest.conditions
;
import
emu.grasscutter.game.quest.QuestValue
;
import
emu.grasscutter.data.excels.QuestData.QuestCondition
;
import
emu.grasscutter.game.quest.GameQuest
;
import
emu.grasscutter.game.quest.enums.QuestTrigger
;
import
emu.grasscutter.game.quest.handlers.QuestBaseHandler
;
@QuestValue
(
QuestTrigger
.
QUEST_COND_STATE_EQUAL
)
public
class
ConditionStateEqual
extends
QuestBaseHandler
{
@Override
public
boolean
execute
(
GameQuest
quest
,
QuestCondition
condition
,
int
...
params
)
{
GameQuest
checkQuest
=
quest
.
getOwner
().
getQuestManager
().
getQuestById
(
params
[
0
]);
if
(
checkQuest
!=
null
)
{
return
checkQuest
.
getState
().
getValue
()
==
params
[
1
];
}
return
false
;
}
}
Prev
1
…
6
7
8
9
10
11
12
13
14
…
21
Next
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