diff --git a/proto/VehicleInteractReq.proto b/proto/VehicleInteractReq.proto new file mode 100644 index 000000000..c94bfcf65 --- /dev/null +++ b/proto/VehicleInteractReq.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +option java_package = "emu.grasscutter.net.proto"; + +import "VehicleInteractType.proto"; + +message VehicleInteractReq { + uint32 entity_id = 1; + VehicleInteractType interact_type = 2; + uint32 pos = 3; +} diff --git a/proto/VehicleInteractRsp.proto b/proto/VehicleInteractRsp.proto new file mode 100644 index 000000000..62c3a040c --- /dev/null +++ b/proto/VehicleInteractRsp.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +option java_package = "emu.grasscutter.net.proto"; + +import "VehicleInteractType.proto"; +import "VehicleMember.proto"; + +message VehicleInteractRsp { + int32 retcode = 1; + uint32 entity_id = 2; + VehicleInteractType interact_type = 3; + VehicleMember member = 4; +} diff --git a/proto/VehicleInteractType.proto b/proto/VehicleInteractType.proto new file mode 100644 index 000000000..f7e10b6fd --- /dev/null +++ b/proto/VehicleInteractType.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +option java_package = "emu.grasscutter.net.proto"; + +enum VehicleInteractType { + VEHICLE_INTERACT_NONE = 0; + VEHICLE_INTERACT_IN = 1; + VEHICLE_INTERACT_OUT = 2; +} diff --git a/proto/VehicleSpawnReq.proto b/proto/VehicleSpawnReq.proto new file mode 100644 index 000000000..aa4e74c74 --- /dev/null +++ b/proto/VehicleSpawnReq.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +option java_package = "emu.grasscutter.net.proto"; + +import "Vector.proto"; + +message VehicleSpawnReq { + uint32 vehicleId = 1; + uint32 pointId = 2; + Vector pos = 3; + Vector rot = 4; +} diff --git a/proto/VehicleSpawnRsp.proto b/proto/VehicleSpawnRsp.proto new file mode 100644 index 000000000..0904aa5b9 --- /dev/null +++ b/proto/VehicleSpawnRsp.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; + +option java_package = "emu.grasscutter.net.proto"; + +message VehicleSpawnRsp { + uint32 vehicleId = 2; + uint32 entityId = 3; +} diff --git a/proto/VehicleStaminaNotify.proto b/proto/VehicleStaminaNotify.proto new file mode 100644 index 000000000..390174f4c --- /dev/null +++ b/proto/VehicleStaminaNotify.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; + +option java_package = "emu.grasscutter.net.proto"; + +message VehicleStaminaNotify { + uint32 entity_id = 1; + float cur_stamina = 2; +} diff --git a/src/main/java/emu/grasscutter/data/def/GadgetData.java b/src/main/java/emu/grasscutter/data/def/GadgetData.java index 7a071972b..7340e8c2c 100644 --- a/src/main/java/emu/grasscutter/data/def/GadgetData.java +++ b/src/main/java/emu/grasscutter/data/def/GadgetData.java @@ -8,14 +8,15 @@ public class GadgetData extends GameResource { private int Id; private String Type; - private String JsonName; - private boolean IsInteractive; - private String[] Tags; - private String ItemJsonName; - private String InteeIconName; - private long NameTextMapHash; - private int CampID; - + private String JsonName; + private boolean IsInteractive; + private String[] Tags; + private String ItemJsonName; + private String InteeIconName; + private long NameTextMapHash; + private int CampID; + private String LODPatternName; + @Override public int getId() { return this.Id; @@ -53,6 +54,8 @@ public class GadgetData extends GameResource { return CampID; } + public String getLODPatternName() { return LODPatternName; } + @Override public void onLoad() { diff --git a/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java b/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java new file mode 100644 index 000000000..06e5ee14a --- /dev/null +++ b/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java @@ -0,0 +1,124 @@ +package emu.grasscutter.game.entity; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.EntityIdType; +import emu.grasscutter.game.props.PlayerProperty; +import emu.grasscutter.game.world.Scene; + +import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo; +import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair; +import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo; +import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo; +import emu.grasscutter.net.proto.FightPropPairOuterClass.*; +import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo; +import emu.grasscutter.net.proto.PropPairOuterClass.PropPair; +import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType; +import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo; +import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo; +import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo; +import emu.grasscutter.net.proto.VectorOuterClass.Vector; +import emu.grasscutter.net.proto.VehicleInfoOuterClass.*; + +import emu.grasscutter.utils.Position; +import emu.grasscutter.utils.ProtoHelper; + +import it.unimi.dsi.fastutil.ints.Int2FloatMap; +import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; + +public class EntityVehicle extends EntityGadget { + private final Player owner; + private final Int2FloatOpenHashMap fightProp; + + private final Position pos; + private final Position rot; + + private float curStamina; + private final int pointId; + private final int gadgetId; + + public EntityVehicle(Scene scene, Player player, int gadgetId, int pointId, Position pos, Position rot) { + super(scene); + this.owner = player; + this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET); + this.fightProp = new Int2FloatOpenHashMap(); + this.pos = new Position(pos); + this.rot = new Position(rot); + this.gadgetId = gadgetId; + this.pointId = pointId; + this.curStamina = 240; + } + + @Override + public int getGadgetId() { return gadgetId; } + + public Player getOwner() { + return owner; + } + + public float getCurStamina() { return curStamina; } + + public void setCurStamina(float stamina) { this.curStamina = stamina; } + + public int getPointId() { return pointId; } + + @Override + public Int2FloatOpenHashMap getFightProperties() { + return fightProp; + } + + @Override + public Position getPosition() { return this.pos; } + + @Override + public Position getRotation() { + return this.rot; + } + + @Override + public SceneEntityInfo toProto() { + + VehicleInfo vehicle = VehicleInfo.newBuilder() + .setOwnerUid(this.owner.getUid()) + .setCurStamina(getCurStamina()) + .build(); + + EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder() + .setAbilityInfo(AbilitySyncStateInfo.newBuilder()) + .setRendererChangedInfo(EntityRendererChangedInfo.newBuilder()) + .setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(getPosition().toProto())) + .setBornPos(getPosition().toProto()) + .build(); + + SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder() + .setGadgetId(this.getGadgetId()) + .setAuthorityPeerId(this.getOwner().getPeerId()) + .setIsEnableInteract(true) + .setVehicleInfo(vehicle); + + SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder() + .setEntityId(getId()) + .setEntityType(ProtEntityType.PROT_ENTITY_GADGET) + .setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder())) + .addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder()) + .setGadget(gadgetInfo) + .setEntityAuthorityInfo(authority) + .setLifeState(1); + + PropPair pair = PropPair.newBuilder() + .setType(PlayerProperty.PROP_LEVEL.getId()) + .setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 47)) + .build(); + + for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) { + if (entry.getIntKey() == 0) { + continue; + } + FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build(); + entityInfo.addFightPropList(fightProp); + } + + entityInfo.addPropList(pair); + + return entityInfo.build(); + } +} diff --git a/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java b/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java index b65bc5e5c..8b4674d63 100644 --- a/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java +++ b/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java @@ -1171,6 +1171,11 @@ public class PacketOpcodes { public static final int UseWidgetCreateGadgetRsp = 4290; public static final int UseWidgetRetractGadgetReq = 4255; public static final int UseWidgetRetractGadgetRsp = 4297; + public static final int VehicleSpawnReq = 809; + public static final int VehicleSpawnRsp = 865; + public static final int VehicleInteractReq = 862; + public static final int VehicleInteractRsp = 889; + public static final int VehicleStaminaNotify = 866; public static final int ViewCodexReq = 4210; public static final int ViewCodexRsp = 4209; public static final int WatcherAllDataNotify = 2260; diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerVehicleInteractReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerVehicleInteractReq.java new file mode 100644 index 000000000..3baba9c5b --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerVehicleInteractReq.java @@ -0,0 +1,19 @@ +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.VehicleInteractReqOuterClass; + +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketVehicleInteractRsp; + +@Opcodes(PacketOpcodes.VehicleInteractReq) +public class HandlerVehicleInteractReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + VehicleInteractReqOuterClass.VehicleInteractReq req = VehicleInteractReqOuterClass.VehicleInteractReq.parseFrom(payload); + session.send(new PacketVehicleInteractRsp(session.getPlayer(), req.getEntityId(), req.getInteractType())); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerVehicleSpawnReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerVehicleSpawnReq.java new file mode 100644 index 000000000..2d259f738 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerVehicleSpawnReq.java @@ -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.VehicleSpawnReqOuterClass; + +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketVehicleSpawnRsp; + +import emu.grasscutter.utils.Position; + +@Opcodes(PacketOpcodes.VehicleSpawnReq) +public class HandlerVehicleSpawnReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + VehicleSpawnReqOuterClass.VehicleSpawnReq req = VehicleSpawnReqOuterClass.VehicleSpawnReq.parseFrom(payload); + session.send(new PacketVehicleSpawnRsp(session.getPlayer(), req.getVehicleId(), req.getPointId(), new Position(req.getPos()), new Position(req.getRot()))); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleInteractRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleInteractRsp.java new file mode 100644 index 000000000..73476e821 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleInteractRsp.java @@ -0,0 +1,33 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.entity.GameEntity; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.VehicleInteractTypeOuterClass.VehicleInteractType; +import emu.grasscutter.net.proto.VehicleInteractRspOuterClass.VehicleInteractRsp; +import emu.grasscutter.net.proto.VehicleMemberOuterClass.VehicleMember; + +public class PacketVehicleInteractRsp extends BasePacket { + + public PacketVehicleInteractRsp(Player player, int entityId, VehicleInteractType interactType) { + super(PacketOpcodes.VehicleInteractRsp); + VehicleInteractRsp.Builder proto = VehicleInteractRsp.newBuilder(); + + GameEntity vehicle = player.getScene().getEntityById(entityId); + if(vehicle != null) { + proto.setEntityId(vehicle.getId()); + proto.setInteractType(interactType); + + VehicleMember vehicleMember = VehicleMember.newBuilder() + .setUid(player.getUid()) + .setAvatarGuid(player.getTeamManager().getCurrentCharacterGuid()) + .build(); + + proto.setMember(vehicleMember); + } + this.setData(proto.build()); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleSpawnRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleSpawnRsp.java new file mode 100644 index 000000000..69b3d8e6f --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleSpawnRsp.java @@ -0,0 +1,46 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.entity.EntityVehicle; +import emu.grasscutter.game.props.FightProperty; +import emu.grasscutter.game.entity.GameEntity; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.VehicleSpawnRspOuterClass.VehicleSpawnRsp; + +import emu.grasscutter.utils.Position; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; + +public class PacketVehicleSpawnRsp extends BasePacket { + + public PacketVehicleSpawnRsp(Player player, int vehicleId, int pointId, Position pos, Position rot) { + super(PacketOpcodes.VehicleSpawnRsp); + VehicleSpawnRsp.Builder proto = VehicleSpawnRsp.newBuilder(); + + EntityVehicle vehicle = new EntityVehicle(player.getScene(), player, vehicleId, pointId, pos, rot); + + switch (vehicleId) { + // TODO: Not hardcode this. Waverider (skiff) + case 45001001,45001002 -> { + vehicle.addFightProperty(FightProperty.FIGHT_PROP_BASE_HP, 10000); + vehicle.addFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, 100); + vehicle.addFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK, 100); + vehicle.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 10000); + vehicle.addFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE, 0); + vehicle.addFightProperty(FightProperty.FIGHT_PROP_CUR_SPEED, 0); + vehicle.addFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, 0); + vehicle.addFightProperty(FightProperty.FIGHT_PROP_MAX_HP, 10000); + } + default -> {} + } + + player.getScene().addEntity(vehicle); + + proto.setVehicleId(vehicleId); + proto.setEntityId(vehicle.getId()); + + this.setData(proto.build()); + } +}