mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-12-18 09:54:59 +01:00
Merge branch 'development' into unstable
# Conflicts: # src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java # src/main/java/emu/grasscutter/server/packet/recv/HandlerCombatInvocationsNotify.java # src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonEntryInfoReq.java
This commit is contained in:
@@ -1 +1,2 @@
|
|||||||
org.gradle.jvmargs=-Xmx1024m
|
org.gradle.jvmargs=-Xmx1024m
|
||||||
|
# spikehd was here :)
|
||||||
|
|||||||
@@ -1,122 +1,122 @@
|
|||||||
package emu.grasscutter.game.dungeons;
|
package emu.grasscutter.game.dungeons;
|
||||||
|
|
||||||
import emu.grasscutter.GameConstants;
|
import emu.grasscutter.GameConstants;
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.binout.ScenePointEntry;
|
import emu.grasscutter.data.binout.ScenePointEntry;
|
||||||
import emu.grasscutter.data.excels.DungeonData;
|
import emu.grasscutter.data.excels.DungeonData;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.game.props.SceneType;
|
import emu.grasscutter.game.props.SceneType;
|
||||||
import emu.grasscutter.game.quest.enums.QuestTrigger;
|
import emu.grasscutter.game.quest.enums.QuestTrigger;
|
||||||
import emu.grasscutter.game.world.Scene;
|
import emu.grasscutter.game.world.Scene;
|
||||||
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.server.game.BaseGameSystem;
|
import emu.grasscutter.server.game.BaseGameSystem;
|
||||||
import emu.grasscutter.server.game.GameServer;
|
import emu.grasscutter.server.game.GameServer;
|
||||||
import emu.grasscutter.server.packet.send.PacketDungeonEntryInfoRsp;
|
import emu.grasscutter.server.packet.send.PacketDungeonEntryInfoRsp;
|
||||||
import emu.grasscutter.server.packet.send.PacketPlayerEnterDungeonRsp;
|
import emu.grasscutter.server.packet.send.PacketPlayerEnterDungeonRsp;
|
||||||
import emu.grasscutter.utils.Position;
|
import emu.grasscutter.utils.Position;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class DungeonSystem extends BaseGameSystem {
|
public class DungeonSystem extends BaseGameSystem {
|
||||||
private static final BasicDungeonSettleListener basicDungeonSettleObserver =
|
private static final BasicDungeonSettleListener basicDungeonSettleObserver =
|
||||||
new BasicDungeonSettleListener();
|
new BasicDungeonSettleListener();
|
||||||
|
|
||||||
public DungeonSystem(GameServer server) {
|
public DungeonSystem(GameServer server) {
|
||||||
super(server);
|
super(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void getEntryInfo(Player player, int pointId) {
|
public void getEntryInfo(Player player, int pointId, int sceneId) {
|
||||||
ScenePointEntry entry = GameData.getScenePointEntryById(player.getScene().getId(), pointId);
|
ScenePointEntry entry = GameData.getScenePointEntryById(sceneId, pointId);
|
||||||
|
|
||||||
if (entry == null) {
|
if (entry == null) {
|
||||||
// Error
|
// Error
|
||||||
player.sendPacket(new PacketDungeonEntryInfoRsp());
|
player.sendPacket(new PacketDungeonEntryInfoRsp());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
player.sendPacket(new PacketDungeonEntryInfoRsp(player, entry.getPointData()));
|
player.sendPacket(new PacketDungeonEntryInfoRsp(player, entry.getPointData()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean enterDungeon(Player player, int pointId, int dungeonId) {
|
public boolean enterDungeon(Player player, int pointId, int dungeonId) {
|
||||||
DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
|
DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
|
||||||
|
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Grasscutter.getLogger()
|
Grasscutter.getLogger()
|
||||||
.info(
|
.info(
|
||||||
"{}({}) is trying to enter dungeon {}",
|
"{}({}) is trying to enter dungeon {}",
|
||||||
player.getNickname(),
|
player.getNickname(),
|
||||||
player.getUid(),
|
player.getUid(),
|
||||||
dungeonId);
|
dungeonId);
|
||||||
|
|
||||||
int sceneId = data.getSceneId();
|
int sceneId = data.getSceneId();
|
||||||
player.getScene().setPrevScene(sceneId);
|
player.getScene().setPrevScene(sceneId);
|
||||||
|
|
||||||
if (player.getWorld().transferPlayerToScene(player, sceneId, data)) {
|
if (player.getWorld().transferPlayerToScene(player, sceneId, data)) {
|
||||||
player.getScene().addDungeonSettleObserver(basicDungeonSettleObserver);
|
player.getScene().addDungeonSettleObserver(basicDungeonSettleObserver);
|
||||||
player.getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_ENTER_DUNGEON, data.getId());
|
player.getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_ENTER_DUNGEON, data.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
player.getScene().setPrevScenePoint(pointId);
|
player.getScene().setPrevScenePoint(pointId);
|
||||||
player.sendPacket(new PacketPlayerEnterDungeonRsp(pointId, dungeonId));
|
player.sendPacket(new PacketPlayerEnterDungeonRsp(pointId, dungeonId));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** used in tower dungeons handoff */
|
/** used in tower dungeons handoff */
|
||||||
public boolean handoffDungeon(
|
public boolean handoffDungeon(
|
||||||
Player player, int dungeonId, List<DungeonSettleListener> dungeonSettleListeners) {
|
Player player, int dungeonId, List<DungeonSettleListener> dungeonSettleListeners) {
|
||||||
DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
|
DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
|
||||||
|
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Grasscutter.getLogger()
|
Grasscutter.getLogger()
|
||||||
.info(
|
.info(
|
||||||
"{}({}) is trying to enter tower dungeon {}",
|
"{}({}) is trying to enter tower dungeon {}",
|
||||||
player.getNickname(),
|
player.getNickname(),
|
||||||
player.getUid(),
|
player.getUid(),
|
||||||
dungeonId);
|
dungeonId);
|
||||||
|
|
||||||
if (player.getWorld().transferPlayerToScene(player, data.getSceneId(), data)) {
|
if (player.getWorld().transferPlayerToScene(player, data.getSceneId(), data)) {
|
||||||
dungeonSettleListeners.forEach(player.getScene()::addDungeonSettleObserver);
|
dungeonSettleListeners.forEach(player.getScene()::addDungeonSettleObserver);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void exitDungeon(Player player) {
|
public void exitDungeon(Player player) {
|
||||||
Scene scene = player.getScene();
|
Scene scene = player.getScene();
|
||||||
|
|
||||||
if (scene == null || scene.getSceneType() != SceneType.SCENE_DUNGEON) {
|
if (scene == null || scene.getSceneType() != SceneType.SCENE_DUNGEON) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get previous scene
|
// Get previous scene
|
||||||
int prevScene = scene.getPrevScene() > 0 ? scene.getPrevScene() : 3;
|
int prevScene = scene.getPrevScene() > 0 ? scene.getPrevScene() : 3;
|
||||||
|
|
||||||
// Get previous position
|
// Get previous position
|
||||||
DungeonData dungeonData = scene.getDungeonData();
|
DungeonData dungeonData = scene.getDungeonData();
|
||||||
Position prevPos = new Position(GameConstants.START_POSITION);
|
Position prevPos = new Position(GameConstants.START_POSITION);
|
||||||
|
|
||||||
if (dungeonData != null) {
|
if (dungeonData != null) {
|
||||||
ScenePointEntry entry = GameData.getScenePointEntryById(prevScene, scene.getPrevScenePoint());
|
ScenePointEntry entry = GameData.getScenePointEntryById(prevScene, scene.getPrevScenePoint());
|
||||||
|
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
prevPos.set(entry.getPointData().getTranPos());
|
prevPos.set(entry.getPointData().getTranPos());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// clean temp team if it has
|
// clean temp team if it has
|
||||||
player.getTeamManager().cleanTemporaryTeam();
|
player.getTeamManager().cleanTemporaryTeam();
|
||||||
player.getTowerManager().clearEntry();
|
player.getTowerManager().clearEntry();
|
||||||
|
|
||||||
// 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateDailyDungeons() {
|
public void updateDailyDungeons() {
|
||||||
GameData.getScenePointEntries()
|
GameData.getScenePointEntries()
|
||||||
.forEach((id, entry) -> entry.getPointData().updateDailyDungeon());
|
.forEach((id, entry) -> entry.getPointData().updateDailyDungeon());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,191 +1,191 @@
|
|||||||
package emu.grasscutter.server.packet.recv;
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.game.entity.GameEntity;
|
import emu.grasscutter.game.entity.GameEntity;
|
||||||
import emu.grasscutter.game.player.Player;
|
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.PacketOpcodes;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
|
||||||
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
|
import emu.grasscutter.net.proto.CombatInvocationsNotifyOuterClass.CombatInvocationsNotify;
|
||||||
import emu.grasscutter.net.proto.CombatInvocationsNotifyOuterClass.CombatInvocationsNotify;
|
import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
|
||||||
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.EvtAnimatorParameterInfoOuterClass.EvtAnimatorParameterInfo;
|
||||||
import emu.grasscutter.net.proto.EvtAnimatorParameterInfoOuterClass.EvtAnimatorParameterInfo;
|
import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo;
|
||||||
import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo;
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
|
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
|
||||||
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
|
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
|
||||||
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass;
|
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass;
|
||||||
import emu.grasscutter.server.event.entity.EntityMoveEvent;
|
import emu.grasscutter.server.event.entity.EntityMoveEvent;
|
||||||
import emu.grasscutter.server.game.GameSession;
|
import emu.grasscutter.server.game.GameSession;
|
||||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
||||||
import emu.grasscutter.utils.Position;
|
import emu.grasscutter.utils.Position;
|
||||||
|
|
||||||
@Opcodes(PacketOpcodes.CombatInvocationsNotify)
|
@Opcodes(PacketOpcodes.CombatInvocationsNotify)
|
||||||
public class HandlerCombatInvocationsNotify extends PacketHandler {
|
public class HandlerCombatInvocationsNotify extends PacketHandler {
|
||||||
|
|
||||||
private float cachedLandingSpeed = 0;
|
private float cachedLandingSpeed = 0;
|
||||||
private long cachedLandingTimeMillisecond = 0;
|
private long cachedLandingTimeMillisecond = 0;
|
||||||
private boolean monitorLandingEvent = false;
|
private boolean monitorLandingEvent = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
CombatInvocationsNotify notif = CombatInvocationsNotify.parseFrom(payload);
|
CombatInvocationsNotify notif = CombatInvocationsNotify.parseFrom(payload);
|
||||||
for (CombatInvokeEntry entry : notif.getInvokeListList()) {
|
for (CombatInvokeEntry entry : notif.getInvokeListList()) {
|
||||||
// Handle combat invoke
|
// Handle combat invoke
|
||||||
switch (entry.getArgumentType()) {
|
switch (entry.getArgumentType()) {
|
||||||
case COMBAT_TYPE_ARGUMENT_EVT_BEING_HIT -> {
|
case COMBAT_TYPE_ARGUMENT_EVT_BEING_HIT -> {
|
||||||
EvtBeingHitInfo hitInfo = EvtBeingHitInfo.parseFrom(entry.getCombatData());
|
EvtBeingHitInfo hitInfo = EvtBeingHitInfo.parseFrom(entry.getCombatData());
|
||||||
AttackResult attackResult = hitInfo.getAttackResult();
|
AttackResult attackResult = hitInfo.getAttackResult();
|
||||||
Player player = session.getPlayer();
|
Player player = session.getPlayer();
|
||||||
|
|
||||||
// Check if the player is invulnerable.
|
// Check if the player is invulnerable.
|
||||||
if (attackResult.getAttackerId()
|
if (attackResult.getAttackerId()
|
||||||
!= player.getTeamManager().getCurrentAvatarEntity().getId()
|
!= player.getTeamManager().getCurrentAvatarEntity().getId()
|
||||||
&& player.getAbilityManager().isAbilityInvulnerable()) break;
|
&& player.getAbilityManager().isAbilityInvulnerable()) break;
|
||||||
|
|
||||||
// Handle damage
|
// Handle damage
|
||||||
player.getAttackResults().add(attackResult);
|
player.getAttackResults().add(attackResult);
|
||||||
player.getEnergyManager().handleAttackHit(hitInfo);
|
player.getEnergyManager().handleAttackHit(hitInfo);
|
||||||
}
|
}
|
||||||
case COMBAT_TYPE_ARGUMENT_ENTITY_MOVE -> {
|
case COMBAT_TYPE_ARGUMENT_ENTITY_MOVE -> {
|
||||||
// Handle movement
|
// Handle movement
|
||||||
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 player
|
// Move player
|
||||||
MotionInfo motionInfo = moveInfo.getMotionInfo();
|
MotionInfo motionInfo = moveInfo.getMotionInfo();
|
||||||
MotionState motionState = motionInfo.getState();
|
MotionState motionState = motionInfo.getState();
|
||||||
|
|
||||||
// Call entity move event.
|
// Call entity move event.
|
||||||
EntityMoveEvent event =
|
EntityMoveEvent event =
|
||||||
new EntityMoveEvent(
|
new EntityMoveEvent(
|
||||||
entity,
|
entity,
|
||||||
new Position(motionInfo.getPos()),
|
new Position(motionInfo.getPos()),
|
||||||
new Position(motionInfo.getRot()),
|
new Position(motionInfo.getRot()),
|
||||||
motionState);
|
motionState);
|
||||||
event.call();
|
event.call();
|
||||||
|
|
||||||
entity.move(event.getPosition(), event.getRotation());
|
entity.move(event.getPosition(), event.getRotation());
|
||||||
entity.setLastMoveSceneTimeMs(moveInfo.getSceneTime());
|
entity.setLastMoveSceneTimeMs(moveInfo.getSceneTime());
|
||||||
entity.setLastMoveReliableSeq(moveInfo.getReliableSeq());
|
entity.setLastMoveReliableSeq(moveInfo.getReliableSeq());
|
||||||
entity.setMotionState(motionState);
|
entity.setMotionState(motionState);
|
||||||
|
|
||||||
session
|
session
|
||||||
.getPlayer()
|
.getPlayer()
|
||||||
.getStaminaManager()
|
.getStaminaManager()
|
||||||
.handleCombatInvocationsNotify(session, moveInfo, entity);
|
.handleCombatInvocationsNotify(session, moveInfo, entity);
|
||||||
|
|
||||||
// TODO: handle MOTION_FIGHT landing which has a different damage factor
|
// TODO: handle MOTION_FIGHT landing which has a different damage factor
|
||||||
// Also, for plunge attacks, LAND_SPEED is always -30 and is not useful.
|
// Also, for plunge attacks, LAND_SPEED is always -30 and is not useful.
|
||||||
// May need the height when starting plunge attack.
|
// May need the height when starting plunge attack.
|
||||||
|
|
||||||
// MOTION_LAND_SPEED and MOTION_FALL_ON_GROUND arrive in different packets.
|
// MOTION_LAND_SPEED and MOTION_FALL_ON_GROUND arrive in different packets.
|
||||||
// Cache land speed for later use.
|
// Cache land speed for later use.
|
||||||
if (motionState == MotionState.MOTION_STATE_LAND_SPEED) {
|
if (motionState == MotionState.MOTION_STATE_LAND_SPEED) {
|
||||||
cachedLandingSpeed = motionInfo.getSpeed().getY();
|
cachedLandingSpeed = motionInfo.getSpeed().getY();
|
||||||
cachedLandingTimeMillisecond = System.currentTimeMillis();
|
cachedLandingTimeMillisecond = System.currentTimeMillis();
|
||||||
monitorLandingEvent = true;
|
monitorLandingEvent = true;
|
||||||
}
|
}
|
||||||
if (monitorLandingEvent) {
|
if (monitorLandingEvent) {
|
||||||
if (motionState == MotionState.MOTION_STATE_FALL_ON_GROUND) {
|
if (motionState == MotionState.MOTION_STATE_FALL_ON_GROUND) {
|
||||||
monitorLandingEvent = false;
|
monitorLandingEvent = false;
|
||||||
handleFallOnGround(session, entity, motionState);
|
handleFallOnGround(session, entity, motionState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MOTION_STATE_NOTIFY = Dont send to other players
|
// as long as one of these two packets be forwarded to client, the animation of avatar will be interrupted
|
||||||
if (motionState == MotionState.MOTION_STATE_NOTIFY) {
|
if (motionState == MotionState.MOTION_STATE_NOTIFY || motionState == MotionState.MOTION_STATE_FIGHT) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case COMBAT_TYPE_ARGUMENT_ANIMATOR_PARAMETER_CHANGED -> {
|
case COMBAT_TYPE_ARGUMENT_ANIMATOR_PARAMETER_CHANGED -> {
|
||||||
EvtAnimatorParameterInfo paramInfo =
|
var paramInfo =
|
||||||
EvtAnimatorParameterInfo.parseFrom(entry.getCombatData());
|
EvtAnimatorParameterInfo.parseFrom(entry.getCombatData());
|
||||||
if (paramInfo.getIsServerCache()) {
|
if (paramInfo.getIsServerCache()) {
|
||||||
paramInfo = paramInfo.toBuilder().setIsServerCache(false).build();
|
paramInfo = paramInfo.toBuilder().setIsServerCache(false).build();
|
||||||
entry = entry.toBuilder().setCombatData(paramInfo.toByteString()).build();
|
entry = entry.toBuilder().setCombatData(paramInfo.toByteString()).build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default -> {}
|
default -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
session.getPlayer().getCombatInvokeHandler().addEntry(entry.getForwardType(), entry);
|
session.getPlayer().getCombatInvokeHandler().addEntry(entry.getForwardType(), entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleFallOnGround(GameSession session, GameEntity entity, MotionState motionState) {
|
private void handleFallOnGround(GameSession session, GameEntity entity, MotionState motionState) {
|
||||||
if (session.getPlayer().inGodmode()) {
|
if (session.getPlayer().inGodmode()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// People have reported that after plunge attack (client sends a FIGHT instead of
|
// People have reported that after plunge attack (client sends a FIGHT instead of
|
||||||
// FALL_ON_GROUND) they will die
|
// FALL_ON_GROUND) they will die
|
||||||
// if they talk to an NPC (this is when the client sends a FALL_ON_GROUND) without jumping
|
// if they talk to an NPC (this is when the client sends a FALL_ON_GROUND) without jumping
|
||||||
// again.
|
// again.
|
||||||
// A dirty patch: if not received immediately after MOTION_LAND_SPEED, discard this packet.
|
// A dirty patch: if not received immediately after MOTION_LAND_SPEED, discard this packet.
|
||||||
// 200ms seems to be a reasonable delay.
|
// 200ms seems to be a reasonable delay.
|
||||||
int maxDelay = 200;
|
int maxDelay = 200;
|
||||||
long actualDelay = System.currentTimeMillis() - cachedLandingTimeMillisecond;
|
long actualDelay = System.currentTimeMillis() - cachedLandingTimeMillisecond;
|
||||||
Grasscutter.getLogger()
|
Grasscutter.getLogger()
|
||||||
.trace(
|
.trace(
|
||||||
"MOTION_FALL_ON_GROUND received after "
|
"MOTION_FALL_ON_GROUND received after "
|
||||||
+ actualDelay
|
+ actualDelay
|
||||||
+ "/"
|
+ "/"
|
||||||
+ maxDelay
|
+ maxDelay
|
||||||
+ "ms."
|
+ "ms."
|
||||||
+ (actualDelay > maxDelay ? " Discard" : ""));
|
+ (actualDelay > maxDelay ? " Discard" : ""));
|
||||||
if (actualDelay > maxDelay) {
|
if (actualDelay > maxDelay) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
float currentHP = entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
float currentHP = entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
||||||
float maxHP = entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
float maxHP = entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||||
float damageFactor = 0;
|
float damageFactor = 0;
|
||||||
if (cachedLandingSpeed < -23.5) {
|
if (cachedLandingSpeed < -23.5) {
|
||||||
damageFactor = 0.33f;
|
damageFactor = 0.33f;
|
||||||
}
|
}
|
||||||
if (cachedLandingSpeed < -25) {
|
if (cachedLandingSpeed < -25) {
|
||||||
damageFactor = 0.5f;
|
damageFactor = 0.5f;
|
||||||
}
|
}
|
||||||
if (cachedLandingSpeed < -26.5) {
|
if (cachedLandingSpeed < -26.5) {
|
||||||
damageFactor = 0.66f;
|
damageFactor = 0.66f;
|
||||||
}
|
}
|
||||||
if (cachedLandingSpeed < -28) {
|
if (cachedLandingSpeed < -28) {
|
||||||
damageFactor = 1f;
|
damageFactor = 1f;
|
||||||
}
|
}
|
||||||
float damage = maxHP * damageFactor;
|
float damage = maxHP * damageFactor;
|
||||||
float newHP = currentHP - damage;
|
float newHP = currentHP - damage;
|
||||||
if (newHP < 0) {
|
if (newHP < 0) {
|
||||||
newHP = 0;
|
newHP = 0;
|
||||||
}
|
}
|
||||||
if (damageFactor > 0) {
|
if (damageFactor > 0) {
|
||||||
Grasscutter.getLogger()
|
Grasscutter.getLogger()
|
||||||
.debug(
|
.debug(
|
||||||
currentHP
|
currentHP
|
||||||
+ "/"
|
+ "/"
|
||||||
+ maxHP
|
+ maxHP
|
||||||
+ "\tLandingSpeed: "
|
+ "\tLandingSpeed: "
|
||||||
+ cachedLandingSpeed
|
+ cachedLandingSpeed
|
||||||
+ "\tDamageFactor: "
|
+ "\tDamageFactor: "
|
||||||
+ damageFactor
|
+ damageFactor
|
||||||
+ "\tDamage: "
|
+ "\tDamage: "
|
||||||
+ damage
|
+ damage
|
||||||
+ "\tNewHP: "
|
+ "\tNewHP: "
|
||||||
+ newHP);
|
+ newHP);
|
||||||
} else {
|
} else {
|
||||||
Grasscutter.getLogger().trace(currentHP + "/" + maxHP + "\tLandingSpeed: 0\tNo damage");
|
Grasscutter.getLogger().trace(currentHP + "/" + maxHP + "\tLandingSpeed: 0\tNo damage");
|
||||||
}
|
}
|
||||||
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
|
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
|
||||||
entity
|
entity
|
||||||
.getWorld()
|
.getWorld()
|
||||||
.broadcastPacket(
|
.broadcastPacket(
|
||||||
new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
|
new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
|
||||||
if (newHP == 0) {
|
if (newHP == 0) {
|
||||||
session
|
session
|
||||||
.getPlayer()
|
.getPlayer()
|
||||||
.getStaminaManager()
|
.getStaminaManager()
|
||||||
.killAvatar(session, entity, PlayerDieTypeOuterClass.PlayerDieType.PLAYER_DIE_TYPE_FALL);
|
.killAvatar(session, entity, PlayerDieTypeOuterClass.PlayerDieType.PLAYER_DIE_TYPE_FALL);
|
||||||
}
|
}
|
||||||
cachedLandingSpeed = 0;
|
cachedLandingSpeed = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package emu.grasscutter.server.packet.recv;
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
import emu.grasscutter.net.packet.Opcodes;
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
import emu.grasscutter.net.packet.PacketHandler;
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.DungeonEntryInfoReqOuterClass.DungeonEntryInfoReq;
|
import emu.grasscutter.net.proto.DungeonEntryInfoReqOuterClass.DungeonEntryInfoReq;
|
||||||
import emu.grasscutter.server.game.GameSession;
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
|
||||||
@Opcodes(PacketOpcodes.DungeonEntryInfoReq)
|
@Opcodes(PacketOpcodes.DungeonEntryInfoReq)
|
||||||
public class HandlerDungeonEntryInfoReq extends PacketHandler {
|
public class HandlerDungeonEntryInfoReq 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 {
|
||||||
DungeonEntryInfoReq req = DungeonEntryInfoReq.parseFrom(payload);
|
DungeonEntryInfoReq req = DungeonEntryInfoReq.parseFrom(payload);
|
||||||
|
|
||||||
session.getServer().getDungeonSystem().getEntryInfo(session.getPlayer(), req.getPointId());
|
session.getServer().getDungeonSystem().getEntryInfo(session.getPlayer(), req.getPointId(), req.getSceneId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user