Merge branch 'development' into api

# Conflicts:
#	src/main/java/emu/grasscutter/Grasscutter.java
This commit is contained in:
KingRainbow44
2022-04-29 19:31:33 -04:00
74 changed files with 2524 additions and 219 deletions

View File

@@ -0,0 +1,105 @@
package emu.grasscutter.game.dungeons;
import java.util.ArrayList;
import java.util.List;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.MonsterData;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.SceneMonster;
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeBeginNotify;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
public class DungeonChallenge {
private final Scene scene;
private final SceneGroup group;
private int challengeIndex;
private int challengeId;
private boolean success;
private boolean progress;
private int score;
private int objective = 0;
public DungeonChallenge(Scene scene, SceneGroup group) {
this.scene = scene;
this.group = group;
objective += group.monsters.size();
}
public Scene getScene() {
return scene;
}
public SceneGroup getGroup() {
return group;
}
public int getChallengeIndex() {
return challengeIndex;
}
public void setChallengeIndex(int challengeIndex) {
this.challengeIndex = challengeIndex;
}
public int getChallengeId() {
return challengeId;
}
public void setChallengeId(int challengeId) {
this.challengeId = challengeId;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean isSuccess) {
this.success = isSuccess;
}
public boolean inProgress() {
return progress;
}
public int getScore() {
return score;
}
public void start() {
this.progress = true;
getScene().broadcastPacket(new PacketDungeonChallengeBeginNotify(this));
}
public void finish() {
this.progress = false;
getScene().broadcastPacket(new PacketDungeonChallengeFinishNotify(this));
if (this.isSuccess()) {
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_SUCCESS, null);
} else {
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_FAIL, null);
}
}
public void onMonsterDie(EntityMonster entity) {
score = getScore() + 1;
getScene().broadcastPacket(new PacketChallengeDataNotify(this, 1, getScore()));
if (getScore() >= objective) {
this.setSuccess(true);
finish();
}
}
}

View File

@@ -1,6 +1,18 @@
package emu.grasscutter.game.dungeons;
import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.custom.ScenePointEntry;
import emu.grasscutter.data.def.DungeonData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.PacketDungeonEntryInfoRsp;
import emu.grasscutter.server.packet.send.PacketPlayerEnterDungeonRsp;
import emu.grasscutter.utils.Position;
public class DungeonManager {
private final GameServer server;
@@ -12,4 +24,59 @@ public class DungeonManager {
public GameServer getServer() {
return server;
}
public void getEntryInfo(Player player, int pointId) {
ScenePointEntry entry = GameData.getScenePointEntryById(player.getScene().getId(), pointId);
if (entry == null || entry.getPointData().getDungeonIds() == null) {
// Error
player.sendPacket(new PacketDungeonEntryInfoRsp());
return;
}
player.sendPacket(new PacketDungeonEntryInfoRsp(player, entry.getPointData()));
}
public void enterDungeon(Player player, int pointId, int dungeonId) {
DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
if (data == null) {
return;
}
Grasscutter.getLogger().info(player.getNickname() + " is trying to enter dungeon " + dungeonId);
int sceneId = data.getSceneId();
player.getScene().setPrevScene(sceneId);
player.getWorld().transferPlayerToScene(player, sceneId, data);
player.getScene().setPrevScenePoint(pointId);
player.sendPacket(new PacketPlayerEnterDungeonRsp(pointId, dungeonId));
}
public void exitDungeon(Player player) {
if (player.getScene().getSceneType() != SceneType.SCENE_DUNGEON) {
return;
}
// Get previous scene
int prevScene = player.getScene().getPrevScene() > 0 ? player.getScene().getPrevScene() : 3;
// Get previous position
DungeonData dungeonData = player.getScene().getDungeonData();
Position prevPos = new Position(GameConstants.START_POSITION);
if (dungeonData != null) {
ScenePointEntry entry = GameData.getScenePointEntryById(prevScene, player.getScene().getPrevScenePoint());
if (entry != null) {
prevPos.set(entry.getPointData().getTranPos());
}
}
// Transfer player back to world
player.getWorld().transferPlayerToScene(player, prevScene, prevPos);
player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp));
}
}

View File

@@ -0,0 +1,18 @@
package emu.grasscutter.game.entity;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.world.World;
public abstract class EntityBaseGadget extends GameEntity {
public EntityBaseGadget(Scene scene) {
super(scene);
}
public abstract int getGadgetId();
@Override
public void onDeath(int killerId) {
}
}

View File

@@ -23,7 +23,7 @@ import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
public class EntityClientGadget extends EntityGadget {
public class EntityClientGadget extends EntityBaseGadget {
private final Player owner;
private final Position pos;

View File

@@ -1,18 +1,157 @@
package emu.grasscutter.game.entity;
import java.util.Arrays;
import java.util.List;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.GadgetData;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.world.World;
import emu.grasscutter.net.proto.ClientGadgetInfoOuterClass;
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.EntityClientDataOuterClass.EntityClientData;
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
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.WorktopInfoOuterClass.WorktopInfo;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
public abstract class EntityGadget extends GameEntity {
public class EntityGadget extends EntityBaseGadget {
private final GadgetData data;
private final Position pos;
private final Position rot;
private int gadgetId;
private int state;
private IntSet worktopOptions;
public EntityGadget(Scene scene) {
public EntityGadget(Scene scene, int gadgetId, Position pos) {
super(scene);
this.data = GameData.getGadgetDataMap().get(gadgetId);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.gadgetId = gadgetId;
this.pos = pos.clone();
this.rot = new Position();
}
public abstract int getGadgetId();
public GadgetData getGadgetData() {
return data;
}
@Override
public Position getPosition() {
// TODO Auto-generated method stub
return this.pos;
}
@Override
public Position getRotation() {
// TODO Auto-generated method stub
return this.rot;
}
public int getGadgetId() {
return gadgetId;
}
public void setGadgetId(int gadgetId) {
this.gadgetId = gadgetId;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public IntSet getWorktopOptions() {
return worktopOptions;
}
public void addWorktopOptions(int[] options) {
if (this.worktopOptions == null) {
this.worktopOptions = new IntOpenHashSet();
}
Arrays.stream(options).forEach(this.worktopOptions::add);
}
public void removeWorktopOption(int option) {
if (this.worktopOptions == null) {
return;
}
this.worktopOptions.remove(option);
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
// TODO Auto-generated method stub
return null;
}
@Override
public void onDeath(int killerId) {
}
@Override
public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
.setBornPos(Vector.newBuilder())
.build();
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())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(1);
PropPair pair = PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
.build();
entityInfo.addPropList(pair);
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
.setGadgetId(this.getGadgetId())
.setGroupId(this.getGroupId())
.setConfigId(this.getConfigId())
.setGadgetState(this.getState())
.setIsEnableInteract(true)
.setAuthorityPeerId(this.getScene().getWorld().getHostPeerId());
if (this.getGadgetData().getType() == EntityType.Worktop && this.getWorktopOptions() != null) {
WorktopInfo worktop = WorktopInfo.newBuilder()
.addAllOptionList(this.getWorktopOptions())
.build();
gadgetInfo.setWorktop(worktop);
}
entityInfo.setGadget(gadgetInfo);
return entityInfo.build();
}
}

View File

@@ -23,7 +23,7 @@ import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
public class EntityItem extends EntityGadget {
public class EntityItem extends EntityBaseGadget {
private final Position pos;
private final Position rot;

View File

@@ -4,6 +4,7 @@ import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.PropGrowCurve;
import emu.grasscutter.data.def.MonsterCurveData;
import emu.grasscutter.data.def.MonsterData;
import emu.grasscutter.game.dungeons.DungeonChallenge;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
@@ -22,6 +23,7 @@ import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.grasscutter.net.proto.SceneMonsterInfoOuterClass.SceneMonsterInfo;
import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
@@ -36,9 +38,6 @@ public class EntityMonster extends GameEntity {
private final Position bornPos;
private final int level;
private int weaponEntityId;
private int groupId;
private int configId;
private int poseId;
public EntityMonster(Scene scene, MonsterData monsterData, Position pos, int level) {
@@ -103,22 +102,6 @@ public class EntityMonster extends GameEntity {
public boolean isAlive() {
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
}
public int getGroupId() {
return groupId;
}
public void setGroupId(int groupId) {
this.groupId = groupId;
}
public int getConfigId() {
return configId;
}
public void setConfigId(int configId) {
this.configId = configId;
}
public int getPoseId() {
return poseId;
@@ -127,12 +110,18 @@ public class EntityMonster extends GameEntity {
public void setPoseId(int poseId) {
this.poseId = poseId;
}
@Override
public void onDeath(int killerId) {
if (this.getSpawnEntry() != null) {
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
}
if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) {
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, null);
}
if (getScene().getChallenge() != null && getScene().getChallenge().getGroup().id == this.getGroupId()) {
getScene().getChallenge().onMonsterDie(this);
}
}
public void recalcStats() {

View File

@@ -18,24 +18,30 @@ 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.net.proto.VehicleMemberOuterClass.*;
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 {
import java.util.List;
import java.util.ArrayList;
public class EntityVehicle extends EntityBaseGadget {
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;
private float curStamina;
private List<VehicleMember> vehicleMembers;
public EntityVehicle(Scene scene, Player player, int gadgetId, int pointId, Position pos, Position rot) {
super(scene);
this.owner = player;
@@ -46,6 +52,7 @@ public class EntityVehicle extends EntityGadget {
this.gadgetId = gadgetId;
this.pointId = pointId;
this.curStamina = 240;
this.vehicleMembers = new ArrayList<VehicleMember>();
}
@Override
@@ -61,6 +68,8 @@ public class EntityVehicle extends EntityGadget {
public int getPointId() { return pointId; }
public List<VehicleMember> getVehicleMembers() { return vehicleMembers; }
@Override
public Int2FloatOpenHashMap getFightProperties() {
return fightProp;

View File

@@ -17,6 +17,10 @@ public abstract class GameEntity {
private final Scene scene;
private SpawnDataEntry spawnEntry;
private int blockId;
private int configId;
private int groupId;
private MotionState moveState;
private int lastMoveSceneTimeMs;
private int lastMoveReliableSeq;
@@ -96,6 +100,30 @@ public abstract class GameEntity {
return getFightProperties().getOrDefault(prop.getId(), 0f);
}
public int getBlockId() {
return blockId;
}
public void setBlockId(int blockId) {
this.blockId = blockId;
}
public int getConfigId() {
return configId;
}
public void setConfigId(int configId) {
this.configId = configId;
}
public int getGroupId() {
return groupId;
}
public void setGroupId(int groupId) {
this.groupId = groupId;
}
protected MotionInfo getMotionInfo() {
MotionInfo proto = MotionInfo.newBuilder()
.setPos(getPosition().toProto())

View File

@@ -244,6 +244,10 @@ public class GameItem {
return mainPropId;
}
public void setMainPropId(int mainPropId) {
this.mainPropId = mainPropId;
}
public List<Integer> getAppendPropIdList() {
return appendPropIdList;
}

View File

@@ -15,7 +15,7 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.def.AvatarSkillDepotData;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityBaseGadget;
import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.game.props.EnterReason;
import emu.grasscutter.game.props.FightProperty;
@@ -54,7 +54,7 @@ public class TeamManager {
@Transient private TeamInfo mpTeam;
@Transient private int entityId;
@Transient private final List<EntityAvatar> avatars;
@Transient private final Set<EntityGadget> gadgets;
@Transient private final Set<EntityBaseGadget> gadgets;
@Transient private final IntSet teamResonances;
@Transient private final IntSet teamResonancesConfig;
@@ -141,7 +141,7 @@ public class TeamManager {
this.entityId = entityId;
}
public Set<EntityGadget> getGadgets() {
public Set<EntityBaseGadget> getGadgets() {
return gadgets;
}

View File

@@ -0,0 +1,93 @@
package emu.grasscutter.game.props;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public enum EntityType {
None (0),
Avatar (1),
Monster (2),
Bullet (3),
AttackPhyisicalUnit (4),
AOE (5),
Camera (6),
EnviroArea (7),
Equip (8),
MonsterEquip (9),
Grass (10),
Level (11),
NPC (12),
TransPointFirst (13),
TransPointFirstGadget (14),
TransPointSecond (15),
TransPointSecondGadget (16),
DropItem (17),
Field (18),
Gadget (19),
Water (20),
GatherPoint (21),
GatherObject (22),
AirflowField (23),
SpeedupField (24),
Gear (25),
Chest (26),
EnergyBall (27),
ElemCrystal (28),
Timeline (29),
Worktop (30),
Team (31),
Platform (32),
AmberWind (33),
EnvAnimal (34),
SealGadget (35),
Tree (36),
Bush (37),
QuestGadget (38),
Lightning (39),
RewardPoint (40),
RewardStatue (41),
MPLevel (42),
WindSeed (43),
MpPlayRewardPoint (44),
ViewPoint (45),
RemoteAvatar (46),
GeneralRewardPoint (47),
PlayTeam (48),
OfferingGadget (49),
EyePoint (50),
MiracleRing (51),
Foundation (52),
WidgetGadget (53),
PlaceHolder (99);
private final int value;
private static final Int2ObjectMap<EntityType> map = new Int2ObjectOpenHashMap<>();
private static final Map<String, EntityType> stringMap = new HashMap<>();
static {
Stream.of(values()).forEach(e -> {
map.put(e.getValue(), e);
stringMap.put(e.name(), e);
});
}
private EntityType(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static EntityType getTypeByValue(int value) {
return map.getOrDefault(value, None);
}
public static EntityType getTypeByName(String name) {
return stringMap.getOrDefault(name, None);
}
}

View File

@@ -3,9 +3,11 @@ package emu.grasscutter.game.world;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameDepot;
import emu.grasscutter.data.def.DungeonData;
import emu.grasscutter.data.def.MonsterData;
import emu.grasscutter.data.def.SceneData;
import emu.grasscutter.data.def.WorldLevelData;
import emu.grasscutter.game.dungeons.DungeonChallenge;
import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.player.TeamInfo;
@@ -17,6 +19,13 @@ import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
import emu.grasscutter.scripts.SceneScriptManager;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneBlock;
import emu.grasscutter.scripts.data.SceneGadget;
import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
@@ -36,12 +45,19 @@ public class Scene {
private final Set<SpawnDataEntry> spawnedEntities;
private final Set<SpawnDataEntry> deadSpawnedEntities;
private final Set<SceneBlock> loadedBlocks;
private boolean dontDestroyWhenEmpty;
private int time;
private ClimateType climate;
private int weather;
private SceneScriptManager scriptManager;
private DungeonChallenge challenge;
private DungeonData dungeonData;
private int prevScene; // Id of the previous scene
private int prevScenePoint;
public Scene(World world, SceneData sceneData) {
this.world = world;
this.sceneData = sceneData;
@@ -50,9 +66,12 @@ public class Scene {
this.time = 8 * 60;
this.climate = ClimateType.CLIMATE_SUNNY;
this.prevScene = 3;
this.spawnedEntities = new HashSet<>();
this.deadSpawnedEntities = new HashSet<>();
this.loadedBlocks = new HashSet<>();
this.scriptManager = new SceneScriptManager(this);
}
public int getId() {
@@ -111,6 +130,22 @@ public class Scene {
this.weather = weather;
}
public int getPrevScene() {
return prevScene;
}
public void setPrevScene(int prevScene) {
this.prevScene = prevScene;
}
public int getPrevScenePoint() {
return prevScenePoint;
}
public void setPrevScenePoint(int prevPoint) {
this.prevScenePoint = prevPoint;
}
public boolean dontDestroyWhenEmpty() {
return dontDestroyWhenEmpty;
}
@@ -119,6 +154,10 @@ public class Scene {
this.dontDestroyWhenEmpty = dontDestroyWhenEmpty;
}
public Set<SceneBlock> getLoadedBlocks() {
return loadedBlocks;
}
public Set<SpawnDataEntry> getSpawnedEntities() {
return spawnedEntities;
}
@@ -127,6 +166,29 @@ public class Scene {
return deadSpawnedEntities;
}
public SceneScriptManager getScriptManager() {
return scriptManager;
}
public DungeonData getDungeonData() {
return dungeonData;
}
public void setDungeonData(DungeonData dungeonData) {
if (this.dungeonData != null || this.getSceneType() != SceneType.SCENE_DUNGEON || dungeonData.getSceneId() != this.getId()) {
return;
}
this.dungeonData = dungeonData;
}
public DungeonChallenge getChallenge() {
return challenge;
}
public void setChallenge(DungeonChallenge challenge) {
this.challenge = challenge;
}
public boolean isInScene(GameEntity entity) {
return this.entities.containsKey(entity.getId());
}
@@ -151,6 +213,11 @@ public class Scene {
}
public synchronized void removePlayer(Player player) {
// Remove from challenge if leaving
if (this.getChallenge() != null && this.getChallenge().inProgress()) {
player.sendPacket(new PacketDungeonChallengeFinishNotify(this.getChallenge()));
}
// Remove player from scene
getPlayers().remove(player);
player.setScene(null);
@@ -159,12 +226,12 @@ public class Scene {
this.removePlayerAvatars(player);
// Remove player gadgets
for (EntityGadget gadget : player.getTeamManager().getGadgets()) {
for (EntityBaseGadget gadget : player.getTeamManager().getGadgets()) {
this.removeEntity(gadget);
}
// Deregister scene if not in use
if (this.getEntities().size() <= 0 && !this.dontDestroyWhenEmpty()) {
if (this.getPlayerCount() <= 0 && !this.dontDestroyWhenEmpty()) {
this.getWorld().deregisterScene(this);
}
}
@@ -279,6 +346,11 @@ public class Scene {
}
}
// Sanity check
if (target.getFightProperties() == null) {
return;
}
// Lose hp
target.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -result.getDamage());
@@ -314,7 +386,15 @@ public class Scene {
}
public void onTick() {
this.checkSpawns();
if (this.getScriptManager().isInit()) {
this.checkBlocks();
} else {
// TEMPORARY
this.checkSpawns();
}
// Triggers
this.getScriptManager().onTick();
}
// TODO - Test
@@ -387,6 +467,68 @@ public class Scene {
}
}
public void checkBlocks() {
Set<SceneBlock> visible = new HashSet<>();
for (Player player : this.getPlayers()) {
for (SceneBlock block : getScriptManager().getBlocks()) {
if (!block.contains(player.getPos())) {
continue;
}
visible.add(block);
}
}
Iterator<SceneBlock> it = this.getLoadedBlocks().iterator();
while (it.hasNext()) {
SceneBlock block = it.next();
if (!visible.contains(block)) {
it.remove();
onUnloadBlock(block);
}
}
for (SceneBlock block : visible) {
if (!this.getLoadedBlocks().contains(block)) {
this.onLoadBlock(block);
this.getLoadedBlocks().add(block);
}
}
}
// TODO optimize
public void onLoadBlock(SceneBlock block) {
for (SceneGroup group : block.groups) {
// We load the script files for the groups here
if (!group.isLoaded()) {
this.getScriptManager().loadGroupFromScript(group);
}
group.triggers.forEach(getScriptManager()::registerTrigger);
}
// Spawn gadgets AFTER triggers are added
for (SceneGroup group : block.groups) {
this.getScriptManager().spawnGadgetsInGroup(group);
}
}
public void onUnloadBlock(SceneBlock block) {
List<GameEntity> toRemove = this.getEntities().values().stream().filter(e -> e.getBlockId() == block.id).toList();
if (toRemove.size() > 0) {
toRemove.stream().forEach(this::removeEntityDirectly);
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_REMOVE));
}
for (SceneGroup group : block.groups) {
group.triggers.forEach(getScriptManager()::deregisterTrigger);
}
}
// Gadgets
public void onPlayerCreateGadget(EntityClientGadget gadget) {

View File

@@ -17,14 +17,16 @@ import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.DungeonData;
import emu.grasscutter.data.def.SceneData;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.EntityClientGadget;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityBaseGadget;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType;
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
import emu.grasscutter.scripts.data.SceneConfig;
import emu.grasscutter.server.packet.send.PacketDelTeamEntityNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
@@ -212,6 +214,14 @@ public class World implements Iterable<Player> {
}
public boolean transferPlayerToScene(Player player, int sceneId, Position pos) {
return transferPlayerToScene(player, sceneId, null, pos);
}
public boolean transferPlayerToScene(Player player, int sceneId, DungeonData data) {
return transferPlayerToScene(player, sceneId, data, null);
}
public boolean transferPlayerToScene(Player player, int sceneId, DungeonData dungeonData, Position pos) {
if (GameData.getSceneDataMap().get(sceneId) == null) {
return false;
}
@@ -224,25 +234,51 @@ public class World implements Iterable<Player> {
// Dont deregister scenes if the player is going to tp back into them
if (oldScene.getId() == sceneId) {
oldScene.setDontDestroyWhenEmpty(true);
}
}
oldScene.removePlayer(player);
}
Scene newScene = this.getSceneById(sceneId);
newScene.setDungeonData(dungeonData);
newScene.addPlayer(player);
player.getPos().set(pos);
// Dungeon
SceneConfig config = newScene.getScriptManager().getConfig();
if (pos == null && config != null) {
if (config.born_pos != null) {
pos = newScene.getScriptManager().getConfig().born_pos;
}
if (config.born_rot != null) {
player.getRotation().set(config.born_rot);
}
}
// Set player position
if (pos == null) {
pos = player.getPos();
}
player.getPos().set(pos);
if (oldScene != null) {
newScene.setPrevScene(oldScene.getId());
oldScene.setDontDestroyWhenEmpty(false);
}
// Teleport packet
if (oldScene == newScene) {
player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.ENTER_GOTO, EnterReason.TransPoint, sceneId, pos));
} else {
player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.ENTER_JUMP, EnterReason.TransPoint, sceneId, pos));
// Get enter types
EnterType enterType = EnterType.ENTER_JUMP;
EnterReason enterReason = EnterReason.TransPoint;
if (dungeonData != null) {
enterType = EnterType.ENTER_DUNGEON;
enterReason = EnterReason.DungeonEnter;
} else if (oldScene == newScene) {
enterType = EnterType.ENTER_GOTO;
}
// Teleport packet
player.sendPacket(new PacketPlayerEnterSceneNotify(player, enterType, enterReason, sceneId, pos));
return true;
}