mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-29 14:26:10 +00:00
commit
a7312ce054
@ -68,6 +68,8 @@ public class GameData {
|
|||||||
private static final Int2ObjectMap<ShopGoodsData> shopGoodsDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<ShopGoodsData> shopGoodsDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<CombineData> combineDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<CombineData> combineDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<RewardPreviewData> rewardPreviewDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<RewardPreviewData> rewardPreviewDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
private static final Int2ObjectMap<TowerFloorData> towerFloorDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
private static final Int2ObjectMap<TowerLevelData> towerLevelDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
// Cache
|
// Cache
|
||||||
private static Map<Integer, List<Integer>> fetters = new HashMap<>();
|
private static Map<Integer, List<Integer>> fetters = new HashMap<>();
|
||||||
@ -311,4 +313,11 @@ public class GameData {
|
|||||||
public static Int2ObjectMap<CombineData> getCombineDataMap() {
|
public static Int2ObjectMap<CombineData> getCombineDataMap() {
|
||||||
return combineDataMap;
|
return combineDataMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Int2ObjectMap<TowerFloorData> getTowerFloorDataMap(){
|
||||||
|
return towerFloorDataMap;
|
||||||
|
}
|
||||||
|
public static Int2ObjectMap<TowerLevelData> getTowerLevelDataMap(){
|
||||||
|
return towerLevelDataMap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
73
src/main/java/emu/grasscutter/data/def/TowerFloorData.java
Normal file
73
src/main/java/emu/grasscutter/data/def/TowerFloorData.java
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package emu.grasscutter.data.def;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.GameResource;
|
||||||
|
import emu.grasscutter.data.ResourceType;
|
||||||
|
|
||||||
|
@ResourceType(name = "TowerFloorExcelConfigData.json")
|
||||||
|
public class TowerFloorData extends GameResource {
|
||||||
|
|
||||||
|
private int FloorId;
|
||||||
|
private int FloorIndex;
|
||||||
|
private int LevelId;
|
||||||
|
private int OverrideMonsterLevel;
|
||||||
|
private int TeamNum;
|
||||||
|
private int FloorLevelConfigId;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return this.FloorId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoad() {
|
||||||
|
super.onLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFloorId() {
|
||||||
|
return FloorId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFloorId(int floorId) {
|
||||||
|
FloorId = floorId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFloorIndex() {
|
||||||
|
return FloorIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFloorIndex(int floorIndex) {
|
||||||
|
FloorIndex = floorIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLevelId() {
|
||||||
|
return LevelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLevelId(int levelId) {
|
||||||
|
LevelId = levelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOverrideMonsterLevel() {
|
||||||
|
return OverrideMonsterLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOverrideMonsterLevel(int overrideMonsterLevel) {
|
||||||
|
OverrideMonsterLevel = overrideMonsterLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTeamNum() {
|
||||||
|
return TeamNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTeamNum(int teamNum) {
|
||||||
|
TeamNum = teamNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFloorLevelConfigId() {
|
||||||
|
return FloorLevelConfigId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFloorLevelConfigId(int floorLevelConfigId) {
|
||||||
|
FloorLevelConfigId = floorLevelConfigId;
|
||||||
|
}
|
||||||
|
}
|
55
src/main/java/emu/grasscutter/data/def/TowerLevelData.java
Normal file
55
src/main/java/emu/grasscutter/data/def/TowerLevelData.java
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package emu.grasscutter.data.def;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.GameResource;
|
||||||
|
import emu.grasscutter.data.ResourceType;
|
||||||
|
|
||||||
|
@ResourceType(name = "TowerLevelExcelConfigData.json")
|
||||||
|
public class TowerLevelData extends GameResource {
|
||||||
|
|
||||||
|
private int ID;
|
||||||
|
private int LevelId;
|
||||||
|
private int LevelIndex;
|
||||||
|
private int DungeonId;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return this.ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoad() {
|
||||||
|
super.onLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getID() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setID(int ID) {
|
||||||
|
this.ID = ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLevelId() {
|
||||||
|
return LevelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLevelId(int levelId) {
|
||||||
|
LevelId = levelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLevelIndex() {
|
||||||
|
return LevelIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLevelIndex(int levelIndex) {
|
||||||
|
LevelIndex = levelIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDungeonId() {
|
||||||
|
return DungeonId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDungeonId(int dungeonId) {
|
||||||
|
DungeonId = dungeonId;
|
||||||
|
}
|
||||||
|
}
|
@ -75,7 +75,8 @@ public class DungeonManager {
|
|||||||
prevPos.set(entry.getPointData().getTranPos());
|
prevPos.set(entry.getPointData().getTranPos());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// clean temp team if it has
|
||||||
|
player.getTeamManager().cleanTemporaryTeam();
|
||||||
// Transfer player back to world
|
// Transfer player back to world
|
||||||
player.getWorld().transferPlayerToScene(player, prevScene, prevPos);
|
player.getWorld().transferPlayerToScene(player, prevScene, prevPos);
|
||||||
player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp));
|
player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp));
|
||||||
|
@ -105,6 +105,11 @@ public class EntityAvatar extends GameEntity {
|
|||||||
this.killedBy = killerId;
|
this.killedBy = killerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onDeath(PlayerDieType dieType, int killerId) {
|
||||||
|
this.killedType = dieType;
|
||||||
|
this.killedBy = killerId;
|
||||||
|
}
|
||||||
|
|
||||||
public SceneAvatarInfo getSceneAvatarInfo() {
|
public SceneAvatarInfo getSceneAvatarInfo() {
|
||||||
SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder()
|
SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder()
|
||||||
.setUid(this.getPlayer().getUid())
|
.setUid(this.getPlayer().getUid())
|
||||||
|
@ -1,227 +0,0 @@
|
|||||||
package emu.grasscutter.game.managers.MotionManager;
|
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
|
||||||
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.net.proto.EntityMoveInfoOuterClass;
|
|
||||||
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
|
|
||||||
import emu.grasscutter.net.proto.VectorOuterClass;
|
|
||||||
import emu.grasscutter.server.game.GameSession;
|
|
||||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
|
||||||
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
|
|
||||||
import emu.grasscutter.server.packet.send.PacketPlayerPropNotify;
|
|
||||||
import emu.grasscutter.utils.Position;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.lang.Math;
|
|
||||||
|
|
||||||
public class MotionManager {
|
|
||||||
|
|
||||||
private enum Consumption {
|
|
||||||
None(0),
|
|
||||||
|
|
||||||
// consumers
|
|
||||||
CLIMB_START(-500),
|
|
||||||
CLIMBING(-150),
|
|
||||||
CLIMB_JUMP(-2500),
|
|
||||||
DASH(-1800),
|
|
||||||
SPRINT(-360),
|
|
||||||
FLY(-60),
|
|
||||||
SWIM_DASH_START(-200),
|
|
||||||
SWIM_DASH(-200),
|
|
||||||
SWIMMING(-80),
|
|
||||||
|
|
||||||
// restorers
|
|
||||||
STANDBY(500),
|
|
||||||
RUN(500),
|
|
||||||
WALK(500),
|
|
||||||
STANDBY_MOVE(500);
|
|
||||||
|
|
||||||
public final int amount;
|
|
||||||
Consumption(int amount) {
|
|
||||||
this.amount = amount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private EntityMoveInfoOuterClass.EntityMoveInfo moveInfo;
|
|
||||||
|
|
||||||
private MotionState previousState = MotionState.MOTION_STANDBY;
|
|
||||||
private ArrayList<Position> previousCoordinates = new ArrayList<>();
|
|
||||||
private final Player player;
|
|
||||||
|
|
||||||
private float landSpeed = 0;
|
|
||||||
|
|
||||||
public MotionManager(Player player) {
|
|
||||||
previousCoordinates.add(new Position(0,0,0));
|
|
||||||
this.player = player;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handle(GameSession session, GameEntity entity, EntityMoveInfoOuterClass.EntityMoveInfo moveInfo) {
|
|
||||||
MotionState state = moveInfo.getMotionInfo().getState();
|
|
||||||
setMoveInfo(moveInfo);
|
|
||||||
if (state == MotionState.MOTION_LAND_SPEED) {
|
|
||||||
setLandSpeed(moveInfo.getMotionInfo().getSpeed().getY());
|
|
||||||
}
|
|
||||||
if (state == MotionState.MOTION_FALL_ON_GROUND) {
|
|
||||||
handleFallOnGround(session, entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tick() {
|
|
||||||
if(Grasscutter.getConfig().OpenStamina){
|
|
||||||
EntityMoveInfoOuterClass.EntityMoveInfo mInfo = moveInfo;
|
|
||||||
if (mInfo == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MotionState state = moveInfo.getMotionInfo().getState();
|
|
||||||
Consumption consumption = Consumption.None;
|
|
||||||
|
|
||||||
boolean isMoving = false;
|
|
||||||
VectorOuterClass.Vector posVector = moveInfo.getMotionInfo().getPos();
|
|
||||||
Position currentCoordinates = new Position(posVector.getX(), posVector.getY(), posVector.getZ());
|
|
||||||
|
|
||||||
float diffX = currentCoordinates.getX() - previousCoordinates.get(0).getX();
|
|
||||||
float diffY = currentCoordinates.getY() - previousCoordinates.get(0).getY();
|
|
||||||
float diffZ = currentCoordinates.getZ() - previousCoordinates.get(0).getZ();
|
|
||||||
|
|
||||||
if (Math.abs(diffX) > 0.3 || Math.abs(diffY) > 0.3 || Math.abs(diffZ) > 0.3) {
|
|
||||||
isMoving = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMoving) {
|
|
||||||
// TODO: refactor these conditions.
|
|
||||||
// CLIMB
|
|
||||||
if (state == MotionState.MOTION_CLIMB) {
|
|
||||||
if (previousState != MotionState.MOTION_CLIMB && previousState != MotionState.MOTION_CLIMB_JUMP) {
|
|
||||||
consumption = Consumption.CLIMB_START;
|
|
||||||
} else {
|
|
||||||
consumption = Consumption.CLIMBING;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// JUMP
|
|
||||||
if (state == MotionState.MOTION_CLIMB_JUMP) {
|
|
||||||
if (previousState != MotionState.MOTION_CLIMB_JUMP) {
|
|
||||||
consumption = Consumption.CLIMB_JUMP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (state == MotionState.MOTION_JUMP) {
|
|
||||||
if (previousState == MotionState.MOTION_CLIMB) {
|
|
||||||
consumption = Consumption.CLIMB_JUMP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// SWIM
|
|
||||||
if (state == MotionState.MOTION_SWIM_MOVE) {
|
|
||||||
consumption = Consumption.SWIMMING;
|
|
||||||
}
|
|
||||||
if (state == MotionState.MOTION_SWIM_DASH) {
|
|
||||||
if (previousState != MotionState.MOTION_SWIM_DASH) {
|
|
||||||
consumption = Consumption.SWIM_DASH_START;
|
|
||||||
} else {
|
|
||||||
consumption = Consumption.SWIM_DASH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// DASH
|
|
||||||
if (state == MotionState.MOTION_DASH) {
|
|
||||||
if (previousState == MotionState.MOTION_DASH) {
|
|
||||||
consumption = Consumption.SPRINT;
|
|
||||||
} else {
|
|
||||||
consumption = Consumption.DASH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// RUN and WALK
|
|
||||||
if (state == MotionState.MOTION_RUN) {
|
|
||||||
consumption = Consumption.RUN;
|
|
||||||
}
|
|
||||||
if (state == MotionState.MOTION_WALK) {
|
|
||||||
consumption = Consumption.WALK;
|
|
||||||
}
|
|
||||||
// FLY
|
|
||||||
if (state == MotionState.MOTION_FLY) {
|
|
||||||
consumption = Consumption.FLY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// STAND
|
|
||||||
if (state == MotionState.MOTION_STANDBY) {
|
|
||||||
consumption = Consumption.STANDBY;
|
|
||||||
}
|
|
||||||
if (state == MotionState.MOTION_STANDBY_MOVE) {
|
|
||||||
consumption = Consumption.STANDBY_MOVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
GameSession session = player.getSession();
|
|
||||||
updateStamina(session, consumption.amount);
|
|
||||||
session.send(new PacketPlayerPropNotify(session.getPlayer(), PlayerProperty.PROP_CUR_PERSIST_STAMINA));
|
|
||||||
|
|
||||||
Grasscutter.getLogger().debug(session.getPlayer().getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) + " " + state + " " + isMoving + " " + consumption + " " + consumption.amount);
|
|
||||||
|
|
||||||
previousState = state;
|
|
||||||
previousCoordinates.add(currentCoordinates);
|
|
||||||
if (previousCoordinates.size() > 3) {
|
|
||||||
previousCoordinates.remove(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateStamina(GameSession session, int amount) {
|
|
||||||
if (amount == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int currentStamina = session.getPlayer().getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
|
||||||
int playerMaxStamina = session.getPlayer().getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
|
||||||
int newStamina = currentStamina + amount;
|
|
||||||
if (newStamina < 0) {
|
|
||||||
newStamina = 0;
|
|
||||||
}
|
|
||||||
if (newStamina > playerMaxStamina) {
|
|
||||||
newStamina = playerMaxStamina;
|
|
||||||
}
|
|
||||||
session.getPlayer().setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMoveInfo(EntityMoveInfoOuterClass.EntityMoveInfo moveInfo) {
|
|
||||||
this.moveInfo = moveInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityMoveInfoOuterClass.EntityMoveInfo getMoveInfo() {
|
|
||||||
return moveInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handleFallOnGround(GameSession session, GameEntity entity) {
|
|
||||||
float currentHP = entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
|
||||||
float maxHP = entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
|
||||||
float damage = 0;
|
|
||||||
Grasscutter.getLogger().debug("LandSpeed: " + landSpeed);
|
|
||||||
if (landSpeed < -23.5) {
|
|
||||||
damage = (float)(maxHP * 0.33);
|
|
||||||
}
|
|
||||||
if (landSpeed < -25) {
|
|
||||||
damage = (float)(maxHP * 0.5);
|
|
||||||
}
|
|
||||||
if (landSpeed < -26.5) {
|
|
||||||
damage = (float)(maxHP * 0.66);
|
|
||||||
}
|
|
||||||
if (landSpeed < -28) {
|
|
||||||
damage = (maxHP * 1);
|
|
||||||
}
|
|
||||||
float newHP = currentHP - damage;
|
|
||||||
if (newHP < 0) {
|
|
||||||
newHP = 0;
|
|
||||||
}
|
|
||||||
Grasscutter.getLogger().debug("Max: " + maxHP + "\tCurr: " + currentHP + "\tDamage: " + damage + "\tnewHP: " + newHP);
|
|
||||||
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
|
|
||||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
|
|
||||||
if (newHP == 0) {
|
|
||||||
entity.getWorld().broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD));
|
|
||||||
session.getPlayer().getScene().removeEntity(entity);
|
|
||||||
entity.onDeath(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLandSpeed(float landSpeed) {
|
|
||||||
this.landSpeed = landSpeed;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,359 @@
|
|||||||
|
package emu.grasscutter.game.managers.MovementManager;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
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.net.proto.EntityMoveInfoOuterClass;
|
||||||
|
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;
|
||||||
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
import emu.grasscutter.server.packet.send.*;
|
||||||
|
import emu.grasscutter.utils.Position;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.lang.Math;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class MovementManager {
|
||||||
|
|
||||||
|
public HashMap<String, HashSet<MotionState>> MotionStatesCategorized = new HashMap<>();
|
||||||
|
|
||||||
|
private enum Consumption {
|
||||||
|
None(0),
|
||||||
|
|
||||||
|
// consume
|
||||||
|
CLIMB_START(-500),
|
||||||
|
CLIMBING(-150),
|
||||||
|
CLIMB_JUMP(-2500),
|
||||||
|
DASH(-1800),
|
||||||
|
SPRINT(-360),
|
||||||
|
FLY(-60),
|
||||||
|
SWIM_DASH_START(-200),
|
||||||
|
SWIM_DASH(-200),
|
||||||
|
SWIMMING(-80),
|
||||||
|
|
||||||
|
// restore
|
||||||
|
STANDBY(500),
|
||||||
|
RUN(500),
|
||||||
|
WALK(500),
|
||||||
|
STANDBY_MOVE(500),
|
||||||
|
POWERED_FLY(500);
|
||||||
|
|
||||||
|
public final int amount;
|
||||||
|
Consumption(int amount) {
|
||||||
|
this.amount = amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private MotionState previousState = MotionState.MOTION_STANDBY;
|
||||||
|
private MotionState currentState = MotionState.MOTION_STANDBY;
|
||||||
|
private Position previousCoordinates = new Position(0, 0, 0);
|
||||||
|
private Position currentCoordinates = new Position(0, 0, 0);
|
||||||
|
|
||||||
|
private final Player player;
|
||||||
|
|
||||||
|
private float landSpeed = 0;
|
||||||
|
private Timer movementManagerTickTimer;
|
||||||
|
private GameSession cachedSession = null;
|
||||||
|
private GameEntity cachedEntity = null;
|
||||||
|
|
||||||
|
private int staminaRecoverDelay = 0;
|
||||||
|
|
||||||
|
public MovementManager(Player player) {
|
||||||
|
previousCoordinates.add(new Position(0,0,0));
|
||||||
|
this.player = player;
|
||||||
|
|
||||||
|
MotionStatesCategorized.put("SWIM", new HashSet<>(Arrays.asList(
|
||||||
|
MotionState.MOTION_SWIM_MOVE,
|
||||||
|
MotionState.MOTION_SWIM_IDLE,
|
||||||
|
MotionState.MOTION_SWIM_DASH,
|
||||||
|
MotionState.MOTION_SWIM_JUMP
|
||||||
|
)));
|
||||||
|
|
||||||
|
MotionStatesCategorized.put("STANDBY", new HashSet<>(Arrays.asList(
|
||||||
|
MotionState.MOTION_STANDBY,
|
||||||
|
MotionState.MOTION_STANDBY_MOVE,
|
||||||
|
MotionState.MOTION_DANGER_STANDBY,
|
||||||
|
MotionState.MOTION_DANGER_STANDBY_MOVE,
|
||||||
|
MotionState.MOTION_LADDER_TO_STANDBY,
|
||||||
|
MotionState.MOTION_JUMP_UP_WALL_FOR_STANDBY
|
||||||
|
)));
|
||||||
|
|
||||||
|
MotionStatesCategorized.put("CLIMB", new HashSet<>(Arrays.asList(
|
||||||
|
MotionState.MOTION_CLIMB,
|
||||||
|
MotionState.MOTION_CLIMB_JUMP,
|
||||||
|
MotionState.MOTION_STANDBY_TO_CLIMB,
|
||||||
|
MotionState.MOTION_LADDER_IDLE,
|
||||||
|
MotionState.MOTION_LADDER_MOVE,
|
||||||
|
MotionState.MOTION_LADDER_SLIP,
|
||||||
|
MotionState.MOTION_STANDBY_TO_LADDER
|
||||||
|
)));
|
||||||
|
|
||||||
|
MotionStatesCategorized.put("FLY", new HashSet<>(Arrays.asList(
|
||||||
|
MotionState.MOTION_FLY,
|
||||||
|
MotionState.MOTION_FLY_IDLE,
|
||||||
|
MotionState.MOTION_FLY_SLOW,
|
||||||
|
MotionState.MOTION_FLY_FAST,
|
||||||
|
MotionState.MOTION_POWERED_FLY
|
||||||
|
)));
|
||||||
|
|
||||||
|
MotionStatesCategorized.put("RUN", new HashSet<>(Arrays.asList(
|
||||||
|
MotionState.MOTION_DASH,
|
||||||
|
MotionState.MOTION_DANGER_DASH,
|
||||||
|
MotionState.MOTION_DASH_BEFORE_SHAKE,
|
||||||
|
MotionState.MOTION_RUN,
|
||||||
|
MotionState.MOTION_DANGER_RUN,
|
||||||
|
MotionState.MOTION_WALK,
|
||||||
|
MotionState.MOTION_DANGER_WALK
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(GameSession session, EntityMoveInfoOuterClass.EntityMoveInfo moveInfo, GameEntity entity) {
|
||||||
|
if (movementManagerTickTimer == null) {
|
||||||
|
movementManagerTickTimer = new Timer();
|
||||||
|
movementManagerTickTimer.scheduleAtFixedRate(new MotionManagerTick(), 0, 200);
|
||||||
|
}
|
||||||
|
// cache info for later use in tick
|
||||||
|
cachedSession = session;
|
||||||
|
cachedEntity = entity;
|
||||||
|
|
||||||
|
MotionInfo motionInfo = moveInfo.getMotionInfo();
|
||||||
|
moveEntity(entity, moveInfo);
|
||||||
|
VectorOuterClass.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;
|
||||||
|
}
|
||||||
|
currentState = motionInfo.getState();
|
||||||
|
Grasscutter.getLogger().debug("" + currentState);
|
||||||
|
handleFallOnGround(motionInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetTimer() {
|
||||||
|
movementManagerTickTimer.cancel();
|
||||||
|
movementManagerTickTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void moveEntity(GameEntity entity, EntityMoveInfoOuterClass.EntityMoveInfo moveInfo) {
|
||||||
|
entity.getPosition().set(moveInfo.getMotionInfo().getPos());
|
||||||
|
entity.getRotation().set(moveInfo.getMotionInfo().getRot());
|
||||||
|
entity.setLastMoveSceneTimeMs(moveInfo.getSceneTime());
|
||||||
|
entity.setLastMoveReliableSeq(moveInfo.getReliableSeq());
|
||||||
|
entity.setMotionState(moveInfo.getMotionInfo().getState());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPlayerMoving() {
|
||||||
|
float diffX = currentCoordinates.getX() - previousCoordinates.getX();
|
||||||
|
float diffY = currentCoordinates.getY() - previousCoordinates.getY();
|
||||||
|
float diffZ = currentCoordinates.getZ() - previousCoordinates.getZ();
|
||||||
|
// Grasscutter.getLogger().debug("isPlayerMoving: " + previousCoordinates + ", " + currentCoordinates + ", " + diffX + ", " + diffY + ", " + diffZ);
|
||||||
|
return Math.abs(diffX) > 0.2 || Math.abs(diffY) > 0.1 || Math.abs(diffZ) > 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getCurrentStamina() {
|
||||||
|
return player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getMaximumStamina() {
|
||||||
|
return player.getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Returns new stamina
|
||||||
|
public int updateStamina(GameSession session, int amount) {
|
||||||
|
int currentStamina = session.getPlayer().getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||||
|
if (amount == 0) {
|
||||||
|
return currentStamina;
|
||||||
|
}
|
||||||
|
int playerMaxStamina = session.getPlayer().getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
||||||
|
int newStamina = currentStamina + amount;
|
||||||
|
if (newStamina < 0) {
|
||||||
|
newStamina = 0;
|
||||||
|
}
|
||||||
|
if (newStamina > playerMaxStamina) {
|
||||||
|
newStamina = playerMaxStamina;
|
||||||
|
}
|
||||||
|
session.getPlayer().setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina);
|
||||||
|
return newStamina;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleFallOnGround(@NotNull MotionInfo motionInfo) {
|
||||||
|
MotionState state = motionInfo.getState();
|
||||||
|
// land speed and fall on ground event arrive in different packets
|
||||||
|
// cache land speed
|
||||||
|
if (state == MotionState.MOTION_LAND_SPEED) {
|
||||||
|
landSpeed = motionInfo.getSpeed().getY();
|
||||||
|
}
|
||||||
|
if (state == MotionState.MOTION_FALL_ON_GROUND) {
|
||||||
|
float currentHP = cachedEntity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
||||||
|
float maxHP = cachedEntity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||||
|
float damage = 0;
|
||||||
|
// Grasscutter.getLogger().debug("LandSpeed: " + landSpeed);
|
||||||
|
if (landSpeed < -23.5) {
|
||||||
|
damage = (float)(maxHP * 0.33);
|
||||||
|
}
|
||||||
|
if (landSpeed < -25) {
|
||||||
|
damage = (float)(maxHP * 0.5);
|
||||||
|
}
|
||||||
|
if (landSpeed < -26.5) {
|
||||||
|
damage = (float)(maxHP * 0.66);
|
||||||
|
}
|
||||||
|
if (landSpeed < -28) {
|
||||||
|
damage = (maxHP * 1);
|
||||||
|
}
|
||||||
|
float newHP = currentHP - damage;
|
||||||
|
if (newHP < 0) {
|
||||||
|
newHP = 0;
|
||||||
|
}
|
||||||
|
// Grasscutter.getLogger().debug("Max: " + maxHP + "\tCurr: " + currentHP + "\tDamage: " + damage + "\tnewHP: " + newHP);
|
||||||
|
cachedEntity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
|
||||||
|
cachedEntity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(cachedEntity, FightProperty.FIGHT_PROP_CUR_HP));
|
||||||
|
if (newHP == 0) {
|
||||||
|
killAvatar(cachedSession, cachedEntity, PlayerDieType.PLAYER_DIE_FALL);
|
||||||
|
}
|
||||||
|
landSpeed = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleDrowning() {
|
||||||
|
int stamina = getCurrentStamina();
|
||||||
|
if (stamina < 10) {
|
||||||
|
boolean isSwimming = MotionStatesCategorized.get("SWIM").contains(currentState);
|
||||||
|
Grasscutter.getLogger().debug(player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) + "/" + player.getProperty(PlayerProperty.PROP_MAX_STAMINA) + "\t" + currentState + "\t" + isSwimming);
|
||||||
|
if (isSwimming && currentState != MotionState.MOTION_SWIM_IDLE) {
|
||||||
|
killAvatar(cachedSession, cachedEntity, PlayerDieType.PLAYER_DIE_DRAWN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void killAvatar(GameSession session, GameEntity entity, PlayerDieType dieType) {
|
||||||
|
cachedSession.send(new PacketAvatarLifeStateChangeNotify(
|
||||||
|
cachedSession.getPlayer().getTeamManager().getCurrentAvatarEntity().getAvatar(),
|
||||||
|
LifeState.LIFE_DEAD,
|
||||||
|
dieType
|
||||||
|
));
|
||||||
|
cachedSession.send(new PacketLifeStateChangeNotify(
|
||||||
|
cachedEntity,
|
||||||
|
LifeState.LIFE_DEAD,
|
||||||
|
dieType
|
||||||
|
));
|
||||||
|
cachedEntity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0);
|
||||||
|
cachedEntity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(cachedEntity, FightProperty.FIGHT_PROP_CUR_HP));
|
||||||
|
entity.getWorld().broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD));
|
||||||
|
session.getPlayer().getScene().removeEntity(entity);
|
||||||
|
((EntityAvatar)entity).onDeath(dieType, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MotionManagerTick extends TimerTask
|
||||||
|
{
|
||||||
|
public void run() {
|
||||||
|
if (Grasscutter.getConfig().OpenStamina) {
|
||||||
|
boolean moving = isPlayerMoving();
|
||||||
|
if (moving || (getCurrentStamina() < getMaximumStamina())) {
|
||||||
|
Grasscutter.getLogger().debug("Player moving: " + moving + ", stamina full: " + (getCurrentStamina() >= getMaximumStamina()) + ", recalculate stamina");
|
||||||
|
Consumption consumption = Consumption.None;
|
||||||
|
|
||||||
|
// TODO: refactor these conditions.
|
||||||
|
if (MotionStatesCategorized.get("CLIMB").contains(currentState)) {
|
||||||
|
if (currentState == MotionState.MOTION_CLIMB) {
|
||||||
|
// CLIMB
|
||||||
|
if (previousState != MotionState.MOTION_CLIMB && previousState != MotionState.MOTION_CLIMB_JUMP) {
|
||||||
|
consumption = Consumption.CLIMB_START;
|
||||||
|
} else {
|
||||||
|
consumption = Consumption.CLIMBING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentState == MotionState.MOTION_CLIMB_JUMP) {
|
||||||
|
if (previousState != MotionState.MOTION_CLIMB_JUMP) {
|
||||||
|
consumption = Consumption.CLIMB_JUMP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentState == MotionState.MOTION_JUMP) {
|
||||||
|
if (previousState == MotionState.MOTION_CLIMB) {
|
||||||
|
consumption = Consumption.CLIMB_JUMP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (MotionStatesCategorized.get("SWIM").contains((currentState))) {
|
||||||
|
// SWIM
|
||||||
|
if (currentState == MotionState.MOTION_SWIM_MOVE) {
|
||||||
|
consumption = Consumption.SWIMMING;
|
||||||
|
}
|
||||||
|
if (currentState == MotionState.MOTION_SWIM_DASH) {
|
||||||
|
if (previousState != MotionState.MOTION_SWIM_DASH) {
|
||||||
|
consumption = Consumption.SWIM_DASH_START;
|
||||||
|
} else {
|
||||||
|
consumption = Consumption.SWIM_DASH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (MotionStatesCategorized.get("RUN").contains(currentState)) {
|
||||||
|
// RUN, DASH and WALK
|
||||||
|
// DASH
|
||||||
|
if (currentState == MotionState.MOTION_DASH) {
|
||||||
|
if (previousState == MotionState.MOTION_DASH) {
|
||||||
|
consumption = Consumption.SPRINT;
|
||||||
|
} else {
|
||||||
|
// cost more to start dashing
|
||||||
|
consumption = Consumption.DASH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// RUN
|
||||||
|
if (currentState == MotionState.MOTION_RUN) {
|
||||||
|
consumption = Consumption.RUN;
|
||||||
|
}
|
||||||
|
// WALK
|
||||||
|
if (currentState == MotionState.MOTION_WALK) {
|
||||||
|
consumption = Consumption.WALK;
|
||||||
|
}
|
||||||
|
} else if (MotionStatesCategorized.get("FLY").contains(currentState)) {
|
||||||
|
// FLY
|
||||||
|
consumption = Consumption.FLY;
|
||||||
|
// POWERED_FLY, e.g. wind tunnel
|
||||||
|
if (currentState == MotionState.MOTION_POWERED_FLY) {
|
||||||
|
consumption = Consumption.POWERED_FLY;
|
||||||
|
}
|
||||||
|
} else if (MotionStatesCategorized.get("STANDBY").contains(currentState)) {
|
||||||
|
// STAND
|
||||||
|
if (currentState == MotionState.MOTION_STANDBY) {
|
||||||
|
consumption = Consumption.STANDBY;
|
||||||
|
}
|
||||||
|
if (currentState == MotionState.MOTION_STANDBY_MOVE) {
|
||||||
|
consumption = Consumption.STANDBY_MOVE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tick triggered
|
||||||
|
handleDrowning();
|
||||||
|
|
||||||
|
if (cachedSession != null) {
|
||||||
|
if (consumption.amount < 0) {
|
||||||
|
staminaRecoverDelay = 0;
|
||||||
|
}
|
||||||
|
if (consumption.amount > 0) {
|
||||||
|
if (staminaRecoverDelay < 5) {
|
||||||
|
staminaRecoverDelay++;
|
||||||
|
consumption = Consumption.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int newStamina = updateStamina(cachedSession, consumption.amount);
|
||||||
|
cachedSession.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA));
|
||||||
|
|
||||||
|
Grasscutter.getLogger().debug(player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) + "/" + player.getProperty(PlayerProperty.PROP_MAX_STAMINA) + "\t" + currentState + "\t" + "isMoving: " + isPlayerMoving() + "\t" + consumption + "(" + consumption.amount + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previousState = currentState;
|
||||||
|
previousCoordinates = new Position(currentCoordinates.getX(),
|
||||||
|
currentCoordinates.getY(), currentCoordinates.getZ());;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,147 @@
|
|||||||
|
package emu.grasscutter.game.managers.SotSManager;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.game.avatar.Avatar;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.game.props.FightProperty;
|
||||||
|
import emu.grasscutter.game.props.PlayerProperty;
|
||||||
|
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.PropChangeReasonOuterClass;
|
||||||
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
// Statue of the Seven Manager
|
||||||
|
public class SotSManager {
|
||||||
|
|
||||||
|
// NOTE: Spring volume balance *1 = fight prop HP *100
|
||||||
|
|
||||||
|
private final Player player;
|
||||||
|
|
||||||
|
public SotSManager(Player player) {
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getIsAutoRecoveryEnabled() {
|
||||||
|
return player.getProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsAutoRecoveryEnabled(boolean enabled) {
|
||||||
|
player.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, enabled ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAutoRecoveryPercentage() {
|
||||||
|
return player.getProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAutoRecoveryPercentage(int percentage) {
|
||||||
|
player.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, percentage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// autoRevive automatically revives all team members.
|
||||||
|
public void autoRevive(GameSession session) {
|
||||||
|
player.getTeamManager().getActiveTeam().forEach(entity -> {
|
||||||
|
boolean isAlive = entity.isAlive();
|
||||||
|
if (!isAlive) {
|
||||||
|
float maxHP = entity.getAvatar().getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||||
|
float newHP = (float)(maxHP * 0.3);
|
||||||
|
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
|
||||||
|
entity.getWorld().broadcastPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void scheduleAutoRecover(GameSession session) {
|
||||||
|
// TODO: play audio effects? possibly client side? - client automatically plays.
|
||||||
|
// delay 2.5 seconds
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
Thread.sleep(2500);
|
||||||
|
autoRecover(session);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Grasscutter.getLogger().error(e.getMessage());
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refillSpringVolume() {
|
||||||
|
// TODO: max spring volume depends on level of the statues in Mondstadt and Liyue.
|
||||||
|
// https://genshin-impact.fandom.com/wiki/Statue_of_The_Seven#:~:text=region%20of%20Inazuma.-,Statue%20Levels,-Upon%20first%20unlocking
|
||||||
|
player.setProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME, 8500000);
|
||||||
|
|
||||||
|
long now = System.currentTimeMillis() / 1000;
|
||||||
|
long secondsSinceLastUsed = now - player.getSpringLastUsed();
|
||||||
|
float percentageRefilled = (float)secondsSinceLastUsed / 15 / 100; // 15s = 1% max volume
|
||||||
|
int maxVolume = player.getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME);
|
||||||
|
int currentVolume = player.getProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME);
|
||||||
|
if (currentVolume < maxVolume) {
|
||||||
|
int volumeRefilled = (int)(percentageRefilled * maxVolume);
|
||||||
|
int newVolume = currentVolume + volumeRefilled;
|
||||||
|
if (currentVolume + volumeRefilled > maxVolume) {
|
||||||
|
newVolume = maxVolume;
|
||||||
|
}
|
||||||
|
player.setProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME, newVolume);
|
||||||
|
}
|
||||||
|
player.setSpringLastUsed(now);
|
||||||
|
player.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// autoRecover checks player setting to see if auto recover is enabled, and refill HP to the predefined level.
|
||||||
|
public void autoRecover(GameSession session) {
|
||||||
|
// TODO: In MP, respect SotS settings from the HOST.
|
||||||
|
boolean isAutoRecoveryEnabled = getIsAutoRecoveryEnabled();
|
||||||
|
int autoRecoverPercentage = getAutoRecoveryPercentage();
|
||||||
|
Grasscutter.getLogger().debug("isAutoRecoveryEnabled: " + isAutoRecoveryEnabled + "\tautoRecoverPercentage: " + autoRecoverPercentage);
|
||||||
|
|
||||||
|
if (isAutoRecoveryEnabled) {
|
||||||
|
player.getTeamManager().getActiveTeam().forEach(entity -> {
|
||||||
|
float maxHP = entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||||
|
float currentHP = entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
||||||
|
if (currentHP == maxHP) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float targetHP = maxHP * autoRecoverPercentage / 100;
|
||||||
|
|
||||||
|
if (targetHP > currentHP) {
|
||||||
|
float needHP = targetHP - currentHP;
|
||||||
|
float needSV = needHP * 100; // convert HP needed to Spring Volume needed
|
||||||
|
|
||||||
|
int sotsSVBalance = player.getProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME);
|
||||||
|
if (sotsSVBalance >= needSV) {
|
||||||
|
// sufficient
|
||||||
|
sotsSVBalance -= needSV;
|
||||||
|
} else {
|
||||||
|
// insufficient balance
|
||||||
|
needSV = sotsSVBalance;
|
||||||
|
sotsSVBalance = 0;
|
||||||
|
}
|
||||||
|
player.setProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME, sotsSVBalance);
|
||||||
|
player.setSpringLastUsed(System.currentTimeMillis() / 1000);
|
||||||
|
|
||||||
|
float newHP = currentHP + needSV / 100; // convert SV to HP
|
||||||
|
|
||||||
|
// TODO: Figure out why client shows current HP instead of added HP.
|
||||||
|
// Say an avatar had 12000 and now has 14000, it should show "2000".
|
||||||
|
// The client always show "+14000" which is incorrect.
|
||||||
|
|
||||||
|
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
|
||||||
|
session.send(new PacketEntityFightPropChangeReasonNotify(entity, FightProperty.FIGHT_PROP_CUR_HP,
|
||||||
|
newHP, List.of(3), PropChangeReasonOuterClass.PropChangeReason.PROP_CHANGE_STATUE_RECOVER,
|
||||||
|
ChangeHpReasonOuterClass.ChangeHpReason.ChangeHpAddStatue));
|
||||||
|
session.send(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
|
||||||
|
|
||||||
|
Avatar avatar = entity.getAvatar();
|
||||||
|
avatar.setCurrentHp(newHP);
|
||||||
|
session.send(new PacketAvatarFightPropUpdateNotify(avatar, FightProperty.FIGHT_PROP_CUR_HP));
|
||||||
|
player.save();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -21,12 +21,14 @@ import emu.grasscutter.game.inventory.GameItem;
|
|||||||
import emu.grasscutter.game.inventory.Inventory;
|
import emu.grasscutter.game.inventory.Inventory;
|
||||||
import emu.grasscutter.game.mail.Mail;
|
import emu.grasscutter.game.mail.Mail;
|
||||||
import emu.grasscutter.game.mail.MailHandler;
|
import emu.grasscutter.game.mail.MailHandler;
|
||||||
import emu.grasscutter.game.managers.MotionManager.MotionManager;
|
import emu.grasscutter.game.managers.MovementManager.MovementManager;
|
||||||
|
import emu.grasscutter.game.managers.SotSManager.SotSManager;
|
||||||
import emu.grasscutter.game.props.ActionReason;
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
import emu.grasscutter.game.props.EntityType;
|
import emu.grasscutter.game.props.EntityType;
|
||||||
import emu.grasscutter.game.props.PlayerProperty;
|
import emu.grasscutter.game.props.PlayerProperty;
|
||||||
import emu.grasscutter.game.shop.ShopLimit;
|
import emu.grasscutter.game.shop.ShopLimit;
|
||||||
import emu.grasscutter.game.managers.MapMarkManager.*;
|
import emu.grasscutter.game.managers.MapMarkManager.*;
|
||||||
|
import emu.grasscutter.game.tower.TowerManager;
|
||||||
import emu.grasscutter.game.world.Scene;
|
import emu.grasscutter.game.world.Scene;
|
||||||
import emu.grasscutter.game.world.World;
|
import emu.grasscutter.game.world.World;
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
@ -88,7 +90,11 @@ public class Player {
|
|||||||
@Transient private MailHandler mailHandler;
|
@Transient private MailHandler mailHandler;
|
||||||
@Transient private MessageHandler messageHandler;
|
@Transient private MessageHandler messageHandler;
|
||||||
|
|
||||||
|
@Transient private SotSManager sotsManager;
|
||||||
|
|
||||||
private TeamManager teamManager;
|
private TeamManager teamManager;
|
||||||
|
|
||||||
|
private TowerManager towerManager;
|
||||||
private PlayerGachaInfo gachaInfo;
|
private PlayerGachaInfo gachaInfo;
|
||||||
private PlayerProfile playerProfile;
|
private PlayerProfile playerProfile;
|
||||||
private boolean showAvatar;
|
private boolean showAvatar;
|
||||||
@ -122,7 +128,9 @@ public class Player {
|
|||||||
@Transient private final InvokeHandler<AbilityInvokeEntry> clientAbilityInitFinishHandler;
|
@Transient private final InvokeHandler<AbilityInvokeEntry> clientAbilityInitFinishHandler;
|
||||||
|
|
||||||
private MapMarksManager mapMarksManager;
|
private MapMarksManager mapMarksManager;
|
||||||
@Transient private MotionManager motionManager;
|
@Transient private MovementManager movementManager;
|
||||||
|
|
||||||
|
private long springLastUsed;
|
||||||
|
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@ -164,7 +172,8 @@ public class Player {
|
|||||||
this.shopLimit = new ArrayList<>();
|
this.shopLimit = new ArrayList<>();
|
||||||
this.messageHandler = null;
|
this.messageHandler = null;
|
||||||
this.mapMarksManager = new MapMarksManager();
|
this.mapMarksManager = new MapMarksManager();
|
||||||
this.motionManager = new MotionManager(this);
|
this.movementManager = new MovementManager(this);
|
||||||
|
this.sotsManager = new SotSManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// On player creation
|
// On player creation
|
||||||
@ -176,6 +185,7 @@ public class Player {
|
|||||||
this.nickname = "Traveler";
|
this.nickname = "Traveler";
|
||||||
this.signature = "";
|
this.signature = "";
|
||||||
this.teamManager = new TeamManager(this);
|
this.teamManager = new TeamManager(this);
|
||||||
|
this.towerManager = new TowerManager(this);
|
||||||
this.birthday = new PlayerBirthday();
|
this.birthday = new PlayerBirthday();
|
||||||
this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, 1);
|
this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, 1);
|
||||||
this.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, 1);
|
this.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, 1);
|
||||||
@ -191,7 +201,8 @@ public class Player {
|
|||||||
this.getRotation().set(0, 307, 0);
|
this.getRotation().set(0, 307, 0);
|
||||||
this.messageHandler = null;
|
this.messageHandler = null;
|
||||||
this.mapMarksManager = new MapMarksManager();
|
this.mapMarksManager = new MapMarksManager();
|
||||||
this.motionManager = new MotionManager(this);
|
this.movementManager = new MovementManager(this);
|
||||||
|
this.sotsManager = new SotSManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getUid() {
|
public int getUid() {
|
||||||
@ -389,6 +400,10 @@ public class Player {
|
|||||||
return this.teamManager;
|
return this.teamManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TowerManager getTowerManager() {
|
||||||
|
return towerManager;
|
||||||
|
}
|
||||||
|
|
||||||
public PlayerGachaInfo getGachaInfo() {
|
public PlayerGachaInfo getGachaInfo() {
|
||||||
return gachaInfo;
|
return gachaInfo;
|
||||||
}
|
}
|
||||||
@ -522,6 +537,14 @@ public class Player {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getSpringLastUsed() {
|
||||||
|
return springLastUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpringLastUsed(long val) {
|
||||||
|
springLastUsed = val;
|
||||||
|
}
|
||||||
|
|
||||||
public SceneLoadState getSceneLoadState() {
|
public SceneLoadState getSceneLoadState() {
|
||||||
return sceneState;
|
return sceneState;
|
||||||
}
|
}
|
||||||
@ -974,7 +997,9 @@ public class Player {
|
|||||||
return mapMarksManager;
|
return mapMarksManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MotionManager getMotionManager() { return motionManager; }
|
public MovementManager getMovementManager() { return movementManager; }
|
||||||
|
|
||||||
|
public SotSManager getSotSManager() { return sotsManager; }
|
||||||
|
|
||||||
public synchronized void onTick() {
|
public synchronized void onTick() {
|
||||||
// Check ping
|
// Check ping
|
||||||
@ -1004,33 +1029,9 @@ public class Player {
|
|||||||
this.resetSendPlayerLocTime();
|
this.resetSendPlayerLocTime();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduleStaminaNotify();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleStaminaNotify() {
|
|
||||||
// stamina tick
|
|
||||||
EntityMoveInfoOuterClass.EntityMoveInfo moveInfo = getMotionManager().getMoveInfo();
|
|
||||||
if (moveInfo == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getMotionManager().getMoveInfo().getMotionInfo().getState() == MotionStateOuterClass.MotionState.MOTION_STANDBY) {
|
|
||||||
if (getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) == getProperty(PlayerProperty.PROP_MAX_STAMINA) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i <= 1000; i+=200) {
|
|
||||||
Timer timer = new Timer();
|
|
||||||
timer.schedule(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
getMotionManager().tick();
|
|
||||||
}
|
|
||||||
}, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void resetSendPlayerLocTime() {
|
public void resetSendPlayerLocTime() {
|
||||||
@ -1054,6 +1055,9 @@ public class Player {
|
|||||||
if (this.getProfile().getUid() == 0) {
|
if (this.getProfile().getUid() == 0) {
|
||||||
this.getProfile().syncWithCharacter(this);
|
this.getProfile().syncWithCharacter(this);
|
||||||
}
|
}
|
||||||
|
if (this.getTowerManager() == null) {
|
||||||
|
this.towerManager = new TowerManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
// Check if player object exists in server
|
// Check if player object exists in server
|
||||||
// TODO - optimize
|
// TODO - optimize
|
||||||
|
@ -18,6 +18,11 @@ public class TeamInfo {
|
|||||||
this.avatars = new ArrayList<>(Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeam);
|
this.avatars = new ArrayList<>(Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TeamInfo(List<Integer> avatars) {
|
||||||
|
this.name = "";
|
||||||
|
this.avatars = avatars;
|
||||||
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,6 @@
|
|||||||
package emu.grasscutter.game.player;
|
package emu.grasscutter.game.player;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import dev.morphia.annotations.Entity;
|
import dev.morphia.annotations.Entity;
|
||||||
import dev.morphia.annotations.Transient;
|
import dev.morphia.annotations.Transient;
|
||||||
@ -24,6 +18,7 @@ import emu.grasscutter.net.packet.BasePacket;
|
|||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType;
|
import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType;
|
||||||
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
|
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
|
||||||
|
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
|
||||||
import emu.grasscutter.server.packet.send.PacketAvatarDieAnimationEndRsp;
|
import emu.grasscutter.server.packet.send.PacketAvatarDieAnimationEndRsp;
|
||||||
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
|
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
|
||||||
import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify;
|
import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify;
|
||||||
@ -59,6 +54,12 @@ public class TeamManager {
|
|||||||
@Transient private final IntSet teamResonances;
|
@Transient private final IntSet teamResonances;
|
||||||
@Transient private final IntSet teamResonancesConfig;
|
@Transient private final IntSet teamResonancesConfig;
|
||||||
|
|
||||||
|
@Transient private int useTemporarilyTeamIndex = -1;
|
||||||
|
/**
|
||||||
|
* Temporary Team for tower
|
||||||
|
*/
|
||||||
|
@Transient private List<TeamInfo> temporaryTeam;
|
||||||
|
|
||||||
public TeamManager() {
|
public TeamManager() {
|
||||||
this.mpTeam = new TeamInfo();
|
this.mpTeam = new TeamInfo();
|
||||||
this.avatars = new ArrayList<>();
|
this.avatars = new ArrayList<>();
|
||||||
@ -124,6 +125,10 @@ public class TeamManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TeamInfo getCurrentTeamInfo() {
|
public TeamInfo getCurrentTeamInfo() {
|
||||||
|
if (useTemporarilyTeamIndex >= 0 &&
|
||||||
|
useTemporarilyTeamIndex < temporaryTeam.size()){
|
||||||
|
return temporaryTeam.get(useTemporarilyTeamIndex);
|
||||||
|
}
|
||||||
if (this.getPlayer().isInMultiplayer()) {
|
if (this.getPlayer().isInMultiplayer()) {
|
||||||
return this.getMpTeam();
|
return this.getMpTeam();
|
||||||
}
|
}
|
||||||
@ -352,6 +357,50 @@ public class TeamManager {
|
|||||||
this.updateTeamEntities(new PacketChangeMpTeamAvatarRsp(getPlayer(), teamInfo));
|
this.updateTeamEntities(new PacketChangeMpTeamAvatarRsp(getPlayer(), teamInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setupTemporaryTeam(List<List<Long>> guidList) {
|
||||||
|
var team = guidList.stream().map(list -> {
|
||||||
|
// Sanity checks
|
||||||
|
if (list.size() == 0 || list.size() > getMaxTeamSize()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set team data
|
||||||
|
LinkedHashSet<Avatar> newTeam = new LinkedHashSet<>();
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(list.get(i));
|
||||||
|
if (avatar == null || newTeam.contains(avatar)) {
|
||||||
|
// Should never happen
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
newTeam.add(avatar);
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert to avatar ids
|
||||||
|
return newTeam.stream()
|
||||||
|
.map(Avatar::getAvatarId)
|
||||||
|
.toList();
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(TeamInfo::new)
|
||||||
|
.toList();
|
||||||
|
this.temporaryTeam = team;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void useTemporaryTeam(int index) {
|
||||||
|
this.useTemporarilyTeamIndex = index;
|
||||||
|
updateTeamEntities(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanTemporaryTeam() {
|
||||||
|
// check if using temporary team
|
||||||
|
if(useTemporarilyTeamIndex < 0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.useTemporarilyTeamIndex = -1;
|
||||||
|
this.temporaryTeam = null;
|
||||||
|
updateTeamEntities(null);
|
||||||
|
}
|
||||||
public synchronized void setCurrentTeam(int teamId) {
|
public synchronized void setCurrentTeam(int teamId) {
|
||||||
//
|
//
|
||||||
if (getPlayer().isInMultiplayer()) {
|
if (getPlayer().isInMultiplayer()) {
|
||||||
@ -420,6 +469,15 @@ public class TeamManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PlayerDieType dieType = deadAvatar.getKilledType();
|
||||||
|
int killedBy = deadAvatar.getKilledBy();
|
||||||
|
|
||||||
|
if (dieType == PlayerDieType.PLAYER_DIE_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?
|
||||||
|
getPlayer().sendPacket(new PacketWorldPlayerDieNotify(dieType, killedBy));
|
||||||
|
} else {
|
||||||
// Replacement avatar
|
// Replacement avatar
|
||||||
EntityAvatar replacement = null;
|
EntityAvatar replacement = null;
|
||||||
int replaceIndex = -1;
|
int replaceIndex = -1;
|
||||||
@ -435,12 +493,13 @@ public class TeamManager {
|
|||||||
|
|
||||||
if (replacement == null) {
|
if (replacement == null) {
|
||||||
// No more living team members...
|
// No more living team members...
|
||||||
getPlayer().sendPacket(new PacketWorldPlayerDieNotify(deadAvatar.getKilledType(), deadAvatar.getKilledBy()));
|
getPlayer().sendPacket(new PacketWorldPlayerDieNotify(dieType, killedBy));
|
||||||
} else {
|
} else {
|
||||||
// Set index and spawn replacement member
|
// Set index and spawn replacement member
|
||||||
this.setCurrentCharacterIndex(replaceIndex);
|
this.setCurrentCharacterIndex(replaceIndex);
|
||||||
getPlayer().getScene().addEntity(replacement);
|
getPlayer().getScene().addEntity(replacement);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Response packet
|
// Response packet
|
||||||
getPlayer().sendPacket(new PacketAvatarDieAnimationEndRsp(deadAvatar.getId(), 0));
|
getPlayer().sendPacket(new PacketAvatarDieAnimationEndRsp(deadAvatar.getId(), 0));
|
||||||
@ -492,11 +551,13 @@ public class TeamManager {
|
|||||||
|
|
||||||
public void respawnTeam() {
|
public void respawnTeam() {
|
||||||
// Make sure all team members are dead
|
// Make sure all team members are dead
|
||||||
for (EntityAvatar entity : getActiveTeam()) {
|
// Drowning needs revive when there may be other team members still alive.
|
||||||
if (entity.isAlive()) {
|
// for (EntityAvatar entity : getActiveTeam()) {
|
||||||
return;
|
// if (entity.isAlive()) {
|
||||||
}
|
// return;
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
player.getMovementManager().resetTimer(); // prevent drowning immediately after respawn
|
||||||
|
|
||||||
// Revive all team members
|
// Revive all team members
|
||||||
for (EntityAvatar entity : getActiveTeam()) {
|
for (EntityAvatar entity : getActiveTeam()) {
|
||||||
|
42
src/main/java/emu/grasscutter/game/tower/TowerManager.java
Normal file
42
src/main/java/emu/grasscutter/game/tower/TowerManager.java
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package emu.grasscutter.game.tower;
|
||||||
|
|
||||||
|
import dev.morphia.annotations.Entity;
|
||||||
|
import dev.morphia.annotations.Transient;
|
||||||
|
import emu.grasscutter.data.GameData;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketTowerEnterLevelRsp;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class TowerManager {
|
||||||
|
@Transient private final Player player;
|
||||||
|
|
||||||
|
public TowerManager(Player player) {
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int currentLevel;
|
||||||
|
private int currentFloor;
|
||||||
|
|
||||||
|
public void teamSelect(int floor, List<List<Long>> towerTeams) {
|
||||||
|
var floorData = GameData.getTowerFloorDataMap().get(floor);
|
||||||
|
|
||||||
|
this.currentFloor = floorData.getFloorId();
|
||||||
|
this.currentLevel = floorData.getLevelId();
|
||||||
|
|
||||||
|
player.getTeamManager().setupTemporaryTeam(towerTeams);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void enterLevel(int enterPointId) {
|
||||||
|
var levelData = GameData.getTowerLevelDataMap().get(currentLevel);
|
||||||
|
var id = levelData.getDungeonId();
|
||||||
|
// use team user choose
|
||||||
|
player.getTeamManager().useTemporaryTeam(0);
|
||||||
|
player.getServer().getDungeonManager()
|
||||||
|
.enterDungeon(player, enterPointId, id);
|
||||||
|
|
||||||
|
player.getSession().send(new PacketTowerEnterLevelRsp(currentFloor, currentLevel));
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,8 @@
|
|||||||
package emu.grasscutter.net.packet;
|
package emu.grasscutter.net.packet;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class PacketOpcodes {
|
public class PacketOpcodes {
|
||||||
// Empty
|
// Empty
|
||||||
public static final int NONE = 0;
|
public static final int NONE = 0;
|
||||||
@ -1566,4 +1569,6 @@ public class PacketOpcodes {
|
|||||||
public static final int UNKNOWN_43 = 8877;
|
public static final int UNKNOWN_43 = 8877;
|
||||||
public static final int UNKNOWN_44 = 8983;
|
public static final int UNKNOWN_44 = 8983;
|
||||||
public static final int UNKNOWN_45 = 943;
|
public static final int UNKNOWN_45 = 943;
|
||||||
|
|
||||||
|
public static final List<Integer> BANNED_PACKETS = Arrays.asList(PacketOpcodes.WindSeedClientNotify, PacketOpcodes.PlayerLuaShellNotify);
|
||||||
}
|
}
|
||||||
|
@ -158,6 +158,12 @@ public class GameSession extends KcpChannel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DO NOT REMOVE (unless we find a way to validate code before sending to client which I don't think we can)
|
||||||
|
// Stop WindSeedClientNotify from being sent for security purposes.
|
||||||
|
if(PacketOpcodes.BANNED_PACKETS.contains(packet.getOpcode())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
if (packet.shouldBuildHeader()) {
|
if (packet.shouldBuildHeader()) {
|
||||||
packet.buildHeader(this.getNextClientSequence());
|
packet.buildHeader(this.getNextClientSequence());
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package emu.grasscutter.server.packet.recv;
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
import emu.grasscutter.game.entity.GameEntity;
|
import emu.grasscutter.game.entity.GameEntity;
|
||||||
import emu.grasscutter.game.managers.MotionManager.MotionManager;
|
|
||||||
import emu.grasscutter.game.props.FightProperty;
|
|
||||||
import emu.grasscutter.game.props.LifeState;
|
|
||||||
import emu.grasscutter.net.packet.Opcodes;
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.CombatInvocationsNotifyOuterClass.CombatInvocationsNotify;
|
import emu.grasscutter.net.proto.CombatInvocationsNotifyOuterClass.CombatInvocationsNotify;
|
||||||
@ -11,9 +8,7 @@ import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
|
|||||||
import emu.grasscutter.net.proto.EntityMoveInfoOuterClass.EntityMoveInfo;
|
import emu.grasscutter.net.proto.EntityMoveInfoOuterClass.EntityMoveInfo;
|
||||||
import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo;
|
import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo;
|
||||||
import emu.grasscutter.net.packet.PacketHandler;
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
|
|
||||||
import emu.grasscutter.server.game.GameSession;
|
import emu.grasscutter.server.game.GameSession;
|
||||||
import emu.grasscutter.server.packet.send.*;
|
|
||||||
|
|
||||||
@Opcodes(PacketOpcodes.CombatInvocationsNotify)
|
@Opcodes(PacketOpcodes.CombatInvocationsNotify)
|
||||||
public class HandlerCombatInvocationsNotify extends PacketHandler {
|
public class HandlerCombatInvocationsNotify extends PacketHandler {
|
||||||
@ -33,13 +28,7 @@ public class HandlerCombatInvocationsNotify extends PacketHandler {
|
|||||||
EntityMoveInfo moveInfo = EntityMoveInfo.parseFrom(entry.getCombatData());
|
EntityMoveInfo moveInfo = EntityMoveInfo.parseFrom(entry.getCombatData());
|
||||||
GameEntity entity = session.getPlayer().getScene().getEntityById(moveInfo.getEntityId());
|
GameEntity entity = session.getPlayer().getScene().getEntityById(moveInfo.getEntityId());
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
//move
|
session.getPlayer().getMovementManager().handle(session, moveInfo, entity);
|
||||||
entity.getPosition().set(moveInfo.getMotionInfo().getPos());
|
|
||||||
entity.getRotation().set(moveInfo.getMotionInfo().getRot());
|
|
||||||
entity.setLastMoveSceneTimeMs(moveInfo.getSceneTime());
|
|
||||||
entity.setLastMoveReliableSeq(moveInfo.getReliableSeq());
|
|
||||||
entity.setMotionState(moveInfo.getMotionInfo().getState());
|
|
||||||
session.getPlayer().getMotionManager().handle(session, entity, moveInfo);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package emu.grasscutter.server.packet.recv;
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.managers.SotSManager.SotSManager;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.game.props.FightProperty;
|
import emu.grasscutter.game.props.FightProperty;
|
||||||
import emu.grasscutter.net.packet.Opcodes;
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
import emu.grasscutter.net.packet.PacketHandler;
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
@ -18,26 +20,12 @@ import java.util.List;
|
|||||||
public class HandlerEnterTransPointRegionNotify extends PacketHandler {
|
public class HandlerEnterTransPointRegionNotify extends PacketHandler {
|
||||||
@Override
|
@Override
|
||||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception{
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception{
|
||||||
session.getPlayer().getTeamManager().getActiveTeam().forEach(entity -> {
|
Player player = session.getPlayer();
|
||||||
boolean isAlive = entity.isAlive();
|
SotSManager sotsManager = player.getSotSManager();
|
||||||
if(entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) != entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP)){
|
|
||||||
Float hp = entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP)-entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
|
||||||
|
|
||||||
session.send(new PacketEntityFightPropUpdateNotify(entity,FightProperty.FIGHT_PROP_MAX_HP));
|
sotsManager.refillSpringVolume();
|
||||||
|
sotsManager.autoRevive(session);
|
||||||
session.send(new PacketEntityFightPropChangeReasonNotify(
|
sotsManager.scheduleAutoRecover(session);
|
||||||
entity, FightProperty.FIGHT_PROP_CUR_HP, hp, List.of(3),
|
// TODO: allow interaction with the SotS?
|
||||||
PropChangeReason.PROP_CHANGE_STATUE_RECOVER, ChangeHpReason.ChangeHpAddStatue));
|
|
||||||
|
|
||||||
entity.setFightProperty(
|
|
||||||
FightProperty.FIGHT_PROP_CUR_HP,
|
|
||||||
entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP)
|
|
||||||
);
|
|
||||||
session.send(new PacketAvatarFightPropUpdateNotify(entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP));
|
|
||||||
if (!isAlive) {
|
|
||||||
entity.getWorld().broadcastPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.TowerEnterLevelReqOuterClass.TowerEnterLevelReq;
|
||||||
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
|
||||||
|
@Opcodes(PacketOpcodes.TowerEnterLevelReq)
|
||||||
|
public class HandlerTowerEnterLevelReq extends PacketHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
|
TowerEnterLevelReq req = TowerEnterLevelReq.parseFrom(payload);
|
||||||
|
|
||||||
|
//session.send(new PacketTowerCurLevelRecordChangeNotify());
|
||||||
|
session.getPlayer().getTowerManager().enterLevel(req.getEnterPointId());
|
||||||
|
|
||||||
|
//session.send(new PacketTowerLevelStarCondNotify());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.TowerTeamOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.TowerTeamSelectReqOuterClass.TowerTeamSelectReq;
|
||||||
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketTowerTeamSelectRsp;
|
||||||
|
|
||||||
|
@Opcodes(PacketOpcodes.TowerTeamSelectReq)
|
||||||
|
public class HandlerTowerTeamSelectReq extends PacketHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
|
TowerTeamSelectReq req = TowerTeamSelectReq.parseFrom(payload);
|
||||||
|
|
||||||
|
var towerTeams = req.getTowerTeamListList().stream()
|
||||||
|
.map(TowerTeamOuterClass.TowerTeam::getAvatarGuidListList)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
session.getPlayer().getTowerManager().teamSelect(req.getFloorId(), towerTeams);
|
||||||
|
|
||||||
|
session.send(new PacketTowerTeamSelectRsp());
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,9 @@ package emu.grasscutter.server.packet.recv;
|
|||||||
import emu.grasscutter.net.packet.Opcodes;
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.packet.PacketHandler;
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
|
import emu.grasscutter.net.proto.WorldPlayerDieNotifyOuterClass;
|
||||||
import emu.grasscutter.server.game.GameSession;
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketWorldPlayerReviveRsp;
|
||||||
|
|
||||||
@Opcodes(PacketOpcodes.WorldPlayerReviveReq)
|
@Opcodes(PacketOpcodes.WorldPlayerReviveReq)
|
||||||
public class HandlerWorldPlayerReviveReq extends PacketHandler {
|
public class HandlerWorldPlayerReviveReq extends PacketHandler {
|
||||||
@ -11,6 +13,7 @@ public class HandlerWorldPlayerReviveReq extends PacketHandler {
|
|||||||
@Override
|
@Override
|
||||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
session.getPlayer().getTeamManager().respawnTeam();
|
session.getPlayer().getTeamManager().respawnTeam();
|
||||||
|
session.send(new PacketWorldPlayerReviveRsp());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,10 @@ import emu.grasscutter.net.packet.BasePacket;
|
|||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.AvatarLifeStateChangeNotifyOuterClass.AvatarLifeStateChangeNotify;
|
import emu.grasscutter.net.proto.AvatarLifeStateChangeNotifyOuterClass.AvatarLifeStateChangeNotify;
|
||||||
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
|
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
|
||||||
|
import emu.grasscutter.net.proto.ServerBuffOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.ServerBuffOuterClass.ServerBuff;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class PacketAvatarLifeStateChangeNotify extends BasePacket {
|
public class PacketAvatarLifeStateChangeNotify extends BasePacket {
|
||||||
|
|
||||||
@ -33,4 +37,26 @@ public class PacketAvatarLifeStateChangeNotify extends BasePacket {
|
|||||||
|
|
||||||
this.setData(proto);
|
this.setData(proto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PacketAvatarLifeStateChangeNotify(Avatar avatar, LifeState lifeState, PlayerDieType dieType) {
|
||||||
|
this(avatar, lifeState, null, "", dieType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketAvatarLifeStateChangeNotify(Avatar avatar, LifeState lifeState, GameEntity sourceEntity,
|
||||||
|
String attackTag, PlayerDieType dieType) {
|
||||||
|
super(PacketOpcodes.AvatarLifeStateChangeNotify);
|
||||||
|
|
||||||
|
AvatarLifeStateChangeNotify.Builder proto = AvatarLifeStateChangeNotify.newBuilder();
|
||||||
|
|
||||||
|
proto.setAvatarGuid(avatar.getGuid());
|
||||||
|
proto.setLifeState(lifeState.getValue());
|
||||||
|
if (sourceEntity != null) {
|
||||||
|
proto.setSourceEntityId(sourceEntity.getId());
|
||||||
|
}
|
||||||
|
proto.setDieType(dieType);
|
||||||
|
proto.setAttackTag((attackTag));
|
||||||
|
|
||||||
|
this.setData(proto.build());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
package emu.grasscutter.server.packet.send;
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.avatar.Avatar;
|
||||||
import emu.grasscutter.game.entity.GameEntity;
|
import emu.grasscutter.game.entity.GameEntity;
|
||||||
import emu.grasscutter.game.props.LifeState;
|
import emu.grasscutter.game.props.LifeState;
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.LifeStateChangeNotifyOuterClass.LifeStateChangeNotify;
|
import emu.grasscutter.net.proto.LifeStateChangeNotifyOuterClass.LifeStateChangeNotify;
|
||||||
|
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
|
||||||
|
import emu.grasscutter.net.proto.ServerBuffOuterClass.ServerBuff;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class PacketLifeStateChangeNotify extends BasePacket {
|
public class PacketLifeStateChangeNotify extends BasePacket {
|
||||||
public PacketLifeStateChangeNotify(GameEntity attacker, GameEntity target, LifeState lifeState) {
|
public PacketLifeStateChangeNotify(GameEntity attacker, GameEntity target, LifeState lifeState) {
|
||||||
@ -29,4 +34,26 @@ public class PacketLifeStateChangeNotify extends BasePacket {
|
|||||||
|
|
||||||
this.setData(proto);
|
this.setData(proto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PacketLifeStateChangeNotify(GameEntity entity, LifeState lifeState, PlayerDieType dieType) {
|
||||||
|
this(entity, lifeState, null, "", dieType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketLifeStateChangeNotify(GameEntity entity, LifeState lifeState, GameEntity sourceEntity,
|
||||||
|
String attackTag, PlayerDieType dieType) {
|
||||||
|
super(PacketOpcodes.LifeStateChangeNotify);
|
||||||
|
|
||||||
|
LifeStateChangeNotify.Builder proto = LifeStateChangeNotify.newBuilder();
|
||||||
|
|
||||||
|
|
||||||
|
proto.setEntityId(entity.getId());
|
||||||
|
proto.setLifeState(lifeState.getValue());
|
||||||
|
if (sourceEntity != null) {
|
||||||
|
proto.setSourceEntityId(sourceEntity.getId());
|
||||||
|
}
|
||||||
|
proto.setAttackTag(attackTag);
|
||||||
|
proto.setDieType(dieType);
|
||||||
|
|
||||||
|
this.setData(proto.build());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,14 +15,7 @@ public class PacketTakeAchievementRewardReq extends BasePacket {
|
|||||||
public PacketTakeAchievementRewardReq(GameSession session) {
|
public PacketTakeAchievementRewardReq(GameSession session) {
|
||||||
super(PacketOpcodes.TakeAchievementRewardReq);
|
super(PacketOpcodes.TakeAchievementRewardReq);
|
||||||
|
|
||||||
List<AchievementInfo> a_list = new ArrayList<>();
|
TakeAchievementRewardReq proto = TakeAchievementRewardReq.newBuilder().build();
|
||||||
a_list.add(AchievementInfo.newBuilder().setId(82044).setStatusValue(2).setCurrent(0).setGoal(1).build());
|
|
||||||
a_list.add(AchievementInfo.newBuilder().setId(81205).setStatusValue(2).setCurrent(0).setGoal(1).build());
|
|
||||||
|
|
||||||
|
|
||||||
TakeAchievementRewardReq proto = TakeAchievementRewardReq.newBuilder()
|
|
||||||
.addAllAList(a_list)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
this.setData(proto);
|
this.setData(proto);
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,28 @@
|
|||||||
package emu.grasscutter.server.packet.send;
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.GameData;
|
||||||
|
import emu.grasscutter.data.def.TowerFloorData;
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.TowerAllDataRspOuterClass.TowerAllDataRsp;
|
import emu.grasscutter.net.proto.TowerAllDataRspOuterClass.TowerAllDataRsp;
|
||||||
import emu.grasscutter.net.proto.TowerCurLevelRecordOuterClass.TowerCurLevelRecord;
|
import emu.grasscutter.net.proto.TowerCurLevelRecordOuterClass.TowerCurLevelRecord;
|
||||||
import emu.grasscutter.net.proto.TowerFloorRecordOuterClass.TowerFloorRecord;
|
import emu.grasscutter.net.proto.TowerFloorRecordOuterClass.TowerFloorRecord;
|
||||||
|
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class PacketTowerAllDataRsp extends BasePacket {
|
public class PacketTowerAllDataRsp extends BasePacket {
|
||||||
|
|
||||||
public PacketTowerAllDataRsp() {
|
public PacketTowerAllDataRsp() {
|
||||||
super(PacketOpcodes.TowerAllDataRsp);
|
super(PacketOpcodes.TowerAllDataRsp);
|
||||||
|
|
||||||
|
var list = GameData.getTowerFloorDataMap().values().stream()
|
||||||
|
.map(TowerFloorData::getFloorId)
|
||||||
|
.map(id -> TowerFloorRecord.newBuilder().setFloorId(id).build())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
TowerAllDataRsp proto = TowerAllDataRsp.newBuilder()
|
TowerAllDataRsp proto = TowerAllDataRsp.newBuilder()
|
||||||
.setTowerScheduleId(29)
|
.setTowerScheduleId(29)
|
||||||
.addTowerFloorRecordList(TowerFloorRecord.newBuilder().setFloorId(1001))
|
.addAllTowerFloorRecordList(list)
|
||||||
.setCurLevelRecord(TowerCurLevelRecord.newBuilder().setIsEmpty(true))
|
.setCurLevelRecord(TowerCurLevelRecord.newBuilder().setIsEmpty(true))
|
||||||
.setNextScheduleChangeTime(Integer.MAX_VALUE)
|
.setNextScheduleChangeTime(Integer.MAX_VALUE)
|
||||||
.putFloorOpenTimeMap(1024, 1630486800)
|
.putFloorOpenTimeMap(1024, 1630486800)
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.TowerEnterLevelRspOuterClass.TowerEnterLevelRsp;
|
||||||
|
|
||||||
|
public class PacketTowerEnterLevelRsp extends BasePacket {
|
||||||
|
|
||||||
|
public PacketTowerEnterLevelRsp(int floorId, int levelIndex) {
|
||||||
|
super(PacketOpcodes.TowerEnterLevelRsp);
|
||||||
|
|
||||||
|
TowerEnterLevelRsp proto = TowerEnterLevelRsp.newBuilder()
|
||||||
|
.setFloorId(floorId)
|
||||||
|
.setLevelIndex(levelIndex)
|
||||||
|
.addTowerBuffIdList(4)
|
||||||
|
.addTowerBuffIdList(28)
|
||||||
|
.addTowerBuffIdList(18)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
this.setData(proto);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.TowerTeamSelectRspOuterClass.TowerTeamSelectRsp;
|
||||||
|
|
||||||
|
public class PacketTowerTeamSelectRsp extends BasePacket {
|
||||||
|
|
||||||
|
public PacketTowerTeamSelectRsp() {
|
||||||
|
super(PacketOpcodes.TowerTeamSelectRsp);
|
||||||
|
|
||||||
|
TowerTeamSelectRsp proto = TowerTeamSelectRsp.newBuilder()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
this.setData(proto);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.game.world.World;
|
||||||
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.WorldPlayerReviveRspOuterClass.WorldPlayerReviveRsp;
|
||||||
|
|
||||||
|
public class PacketWorldPlayerReviveRsp extends BasePacket {
|
||||||
|
|
||||||
|
public PacketWorldPlayerReviveRsp() {
|
||||||
|
super(PacketOpcodes.WorldPlayerReviveRsp);
|
||||||
|
|
||||||
|
WorldPlayerReviveRsp.Builder proto = WorldPlayerReviveRsp.newBuilder();
|
||||||
|
|
||||||
|
this.setData(proto.build());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user