Commit 7fdf7498 authored by omg-xtao's avatar omg-xtao Committed by GitHub
Browse files

Merge branch 'development' into tp

parents eaed1aa0 4c487e22
...@@ -4,6 +4,7 @@ import emu.grasscutter.data.GameData; ...@@ -4,6 +4,7 @@ import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.PropGrowCurve; import emu.grasscutter.data.common.PropGrowCurve;
import emu.grasscutter.data.def.MonsterCurveData; import emu.grasscutter.data.def.MonsterCurveData;
import emu.grasscutter.data.def.MonsterData; import emu.grasscutter.data.def.MonsterData;
import emu.grasscutter.game.dungeons.DungeonChallenge;
import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
...@@ -22,6 +23,7 @@ import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo; ...@@ -22,6 +23,7 @@ import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo; import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.grasscutter.net.proto.SceneMonsterInfoOuterClass.SceneMonsterInfo; import emu.grasscutter.net.proto.SceneMonsterInfoOuterClass.SceneMonsterInfo;
import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo; import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper; import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap; import it.unimi.dsi.fastutil.ints.Int2FloatMap;
...@@ -36,9 +38,6 @@ public class EntityMonster extends GameEntity { ...@@ -36,9 +38,6 @@ public class EntityMonster extends GameEntity {
private final Position bornPos; private final Position bornPos;
private final int level; private final int level;
private int weaponEntityId; private int weaponEntityId;
private int groupId;
private int configId;
private int poseId; private int poseId;
public EntityMonster(Scene scene, MonsterData monsterData, Position pos, int level) { public EntityMonster(Scene scene, MonsterData monsterData, Position pos, int level) {
...@@ -104,22 +103,6 @@ public class EntityMonster extends GameEntity { ...@@ -104,22 +103,6 @@ public class EntityMonster extends GameEntity {
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f; 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() { public int getPoseId() {
return poseId; return poseId;
} }
...@@ -133,6 +116,12 @@ public class EntityMonster extends GameEntity { ...@@ -133,6 +116,12 @@ public class EntityMonster extends GameEntity {
if (this.getSpawnEntry() != null) { if (this.getSpawnEntry() != null) {
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry()); 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() { public void recalcStats() {
......
...@@ -25,7 +25,7 @@ import emu.grasscutter.utils.ProtoHelper; ...@@ -25,7 +25,7 @@ import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap; import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
public class EntityVehicle extends EntityGadget { public class EntityVehicle extends EntityBaseGadget {
private final Player owner; private final Player owner;
private final Int2FloatOpenHashMap fightProp; private final Int2FloatOpenHashMap fightProp;
......
...@@ -17,6 +17,10 @@ public abstract class GameEntity { ...@@ -17,6 +17,10 @@ public abstract class GameEntity {
private final Scene scene; private final Scene scene;
private SpawnDataEntry spawnEntry; private SpawnDataEntry spawnEntry;
private int blockId;
private int configId;
private int groupId;
private MotionState moveState; private MotionState moveState;
private int lastMoveSceneTimeMs; private int lastMoveSceneTimeMs;
private int lastMoveReliableSeq; private int lastMoveReliableSeq;
...@@ -96,6 +100,30 @@ public abstract class GameEntity { ...@@ -96,6 +100,30 @@ public abstract class GameEntity {
return getFightProperties().getOrDefault(prop.getId(), 0f); 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() { protected MotionInfo getMotionInfo() {
MotionInfo proto = MotionInfo.newBuilder() MotionInfo proto = MotionInfo.newBuilder()
.setPos(getPosition().toProto()) .setPos(getPosition().toProto())
......
...@@ -15,7 +15,7 @@ import emu.grasscutter.Grasscutter; ...@@ -15,7 +15,7 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.def.AvatarSkillDepotData; import emu.grasscutter.data.def.AvatarSkillDepotData;
import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar; 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.ElementType;
import emu.grasscutter.game.props.EnterReason; import emu.grasscutter.game.props.EnterReason;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
...@@ -54,7 +54,7 @@ public class TeamManager { ...@@ -54,7 +54,7 @@ public class TeamManager {
@Transient private TeamInfo mpTeam; @Transient private TeamInfo mpTeam;
@Transient private int entityId; @Transient private int entityId;
@Transient private final List<EntityAvatar> avatars; @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 teamResonances;
@Transient private final IntSet teamResonancesConfig; @Transient private final IntSet teamResonancesConfig;
...@@ -141,7 +141,7 @@ public class TeamManager { ...@@ -141,7 +141,7 @@ public class TeamManager {
this.entityId = entityId; this.entityId = entityId;
} }
public Set<EntityGadget> getGadgets() { public Set<EntityBaseGadget> getGadgets() {
return gadgets; return gadgets;
} }
......
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);
}
}
...@@ -3,9 +3,11 @@ package emu.grasscutter.game.world; ...@@ -3,9 +3,11 @@ package emu.grasscutter.game.world;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameDepot; import emu.grasscutter.data.GameDepot;
import emu.grasscutter.data.def.DungeonData;
import emu.grasscutter.data.def.MonsterData; import emu.grasscutter.data.def.MonsterData;
import emu.grasscutter.data.def.SceneData; import emu.grasscutter.data.def.SceneData;
import emu.grasscutter.data.def.WorldLevelData; import emu.grasscutter.data.def.WorldLevelData;
import emu.grasscutter.game.dungeons.DungeonChallenge;
import emu.grasscutter.game.entity.*; import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.player.TeamInfo; import emu.grasscutter.game.player.TeamInfo;
...@@ -17,6 +19,13 @@ import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry; ...@@ -17,6 +19,13 @@ import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry;
import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult; import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType; 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.PacketEntityFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
...@@ -36,12 +45,19 @@ public class Scene { ...@@ -36,12 +45,19 @@ public class Scene {
private final Set<SpawnDataEntry> spawnedEntities; private final Set<SpawnDataEntry> spawnedEntities;
private final Set<SpawnDataEntry> deadSpawnedEntities; private final Set<SpawnDataEntry> deadSpawnedEntities;
private final Set<SceneBlock> loadedBlocks;
private boolean dontDestroyWhenEmpty; private boolean dontDestroyWhenEmpty;
private int time; private int time;
private ClimateType climate; private ClimateType climate;
private int weather; 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) { public Scene(World world, SceneData sceneData) {
this.world = world; this.world = world;
this.sceneData = sceneData; this.sceneData = sceneData;
...@@ -50,9 +66,12 @@ public class Scene { ...@@ -50,9 +66,12 @@ public class Scene {
this.time = 8 * 60; this.time = 8 * 60;
this.climate = ClimateType.CLIMATE_SUNNY; this.climate = ClimateType.CLIMATE_SUNNY;
this.prevScene = 3;
this.spawnedEntities = new HashSet<>(); this.spawnedEntities = new HashSet<>();
this.deadSpawnedEntities = new HashSet<>(); this.deadSpawnedEntities = new HashSet<>();
this.loadedBlocks = new HashSet<>();
this.scriptManager = new SceneScriptManager(this);
} }
public int getId() { public int getId() {
...@@ -111,6 +130,22 @@ public class Scene { ...@@ -111,6 +130,22 @@ public class Scene {
this.weather = weather; 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() { public boolean dontDestroyWhenEmpty() {
return dontDestroyWhenEmpty; return dontDestroyWhenEmpty;
} }
...@@ -119,6 +154,10 @@ public class Scene { ...@@ -119,6 +154,10 @@ public class Scene {
this.dontDestroyWhenEmpty = dontDestroyWhenEmpty; this.dontDestroyWhenEmpty = dontDestroyWhenEmpty;
} }
public Set<SceneBlock> getLoadedBlocks() {
return loadedBlocks;
}
public Set<SpawnDataEntry> getSpawnedEntities() { public Set<SpawnDataEntry> getSpawnedEntities() {
return spawnedEntities; return spawnedEntities;
} }
...@@ -127,6 +166,29 @@ public class Scene { ...@@ -127,6 +166,29 @@ public class Scene {
return deadSpawnedEntities; 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) { public boolean isInScene(GameEntity entity) {
return this.entities.containsKey(entity.getId()); return this.entities.containsKey(entity.getId());
} }
...@@ -151,6 +213,11 @@ public class Scene { ...@@ -151,6 +213,11 @@ public class Scene {
} }
public synchronized void removePlayer(Player player) { 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 // Remove player from scene
getPlayers().remove(player); getPlayers().remove(player);
player.setScene(null); player.setScene(null);
...@@ -159,12 +226,12 @@ public class Scene { ...@@ -159,12 +226,12 @@ public class Scene {
this.removePlayerAvatars(player); this.removePlayerAvatars(player);
// Remove player gadgets // Remove player gadgets
for (EntityGadget gadget : player.getTeamManager().getGadgets()) { for (EntityBaseGadget gadget : player.getTeamManager().getGadgets()) {
this.removeEntity(gadget); this.removeEntity(gadget);
} }
// Deregister scene if not in use // Deregister scene if not in use
if (this.getEntities().size() <= 0 && !this.dontDestroyWhenEmpty()) { if (this.getPlayerCount() <= 0 && !this.dontDestroyWhenEmpty()) {
this.getWorld().deregisterScene(this); this.getWorld().deregisterScene(this);
} }
} }
...@@ -279,6 +346,11 @@ public class Scene { ...@@ -279,6 +346,11 @@ public class Scene {
} }
} }
// Sanity check
if (target.getFightProperties() == null) {
return;
}
// Lose hp // Lose hp
target.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -result.getDamage()); target.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -result.getDamage());
...@@ -314,9 +386,17 @@ public class Scene { ...@@ -314,9 +386,17 @@ public class Scene {
} }
public void onTick() { public void onTick() {
if (this.getScriptManager().isInit()) {
this.checkBlocks();
} else {
// TEMPORARY
this.checkSpawns(); this.checkSpawns();
} }
// Triggers
this.getScriptManager().onTick();
}
// TODO - Test // TODO - Test
public void checkSpawns() { public void checkSpawns() {
SpatialIndex<SpawnGroupEntry> list = GameDepot.getSpawnListById(this.getId()); SpatialIndex<SpawnGroupEntry> list = GameDepot.getSpawnListById(this.getId());
...@@ -387,6 +467,68 @@ public class Scene { ...@@ -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 // Gadgets
public void onPlayerCreateGadget(EntityClientGadget gadget) { public void onPlayerCreateGadget(EntityClientGadget gadget) {
......
...@@ -17,14 +17,16 @@ import emu.grasscutter.game.props.EntityIdType; ...@@ -17,14 +17,16 @@ import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.LifeState; import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.DungeonData;
import emu.grasscutter.data.def.SceneData; import emu.grasscutter.data.def.SceneData;
import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.EntityClientGadget; 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.packet.BasePacket;
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult; import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType; import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType;
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType; 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.PacketDelTeamEntityNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
...@@ -212,6 +214,14 @@ public class World implements Iterable<Player> { ...@@ -212,6 +214,14 @@ public class World implements Iterable<Player> {
} }
public boolean transferPlayerToScene(Player player, int sceneId, Position pos) { 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) { if (GameData.getSceneDataMap().get(sceneId) == null) {
return false; return false;
} }
...@@ -230,19 +240,45 @@ public class World implements Iterable<Player> { ...@@ -230,19 +240,45 @@ public class World implements Iterable<Player> {
} }
Scene newScene = this.getSceneById(sceneId); Scene newScene = this.getSceneById(sceneId);
newScene.setDungeonData(dungeonData);
newScene.addPlayer(player); newScene.addPlayer(player);
// 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); player.getPos().set(pos);
if (oldScene != null) { if (oldScene != null) {
newScene.setPrevScene(oldScene.getId());
oldScene.setDontDestroyWhenEmpty(false); oldScene.setDontDestroyWhenEmpty(false);
} }
// Teleport packet // Get enter types
if (oldScene == newScene) { EnterType enterType = EnterType.ENTER_JUMP;
player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.ENTER_GOTO, EnterReason.TransPoint, sceneId, pos)); EnterReason enterReason = EnterReason.TransPoint;
} else {
player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.ENTER_JUMP, EnterReason.TransPoint, sceneId, pos)); 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; return true;
} }
......
...@@ -355,6 +355,8 @@ public class PacketOpcodes { ...@@ -355,6 +355,8 @@ public class PacketOpcodes {
public static final int EvtAvatarSitDownNotify = 392; public static final int EvtAvatarSitDownNotify = 392;
public static final int EvtAvatarStandUpNotify = 358; public static final int EvtAvatarStandUpNotify = 358;
public static final int EvtAvatarUpdateFocusNotify = 365; public static final int EvtAvatarUpdateFocusNotify = 365;
public static final int EvtAvatarLockChairReq = 354;
public static final int EvtAvatarLockChairRsp = 335;
public static final int EvtBeingHitNotify = 360; public static final int EvtBeingHitNotify = 360;
public static final int EvtBeingHitsCombineNotify = 381; public static final int EvtBeingHitsCombineNotify = 381;
public static final int EvtBulletDeactiveNotify = 388; public static final int EvtBulletDeactiveNotify = 388;
......
...@@ -66,7 +66,7 @@ public final class PluginManager { ...@@ -66,7 +66,7 @@ public final class PluginManager {
Enumeration<JarEntry> entries = jarFile.entries(); Enumeration<JarEntry> entries = jarFile.entries();
while(entries.hasMoreElements()) { while(entries.hasMoreElements()) {
JarEntry entry = entries.nextElement(); JarEntry entry = entries.nextElement();
if(entry.isDirectory() || !entry.getName().endsWith(".class")) continue; if(entry.isDirectory() || !entry.getName().endsWith(".class") || entry.getName().contains("module-info")) continue;
String className = entry.getName().replace(".class", "").replace("/", "."); String className = entry.getName().replace(".class", "").replace("/", ".");
Class<?> clazz = loader.loadClass(className); Class<?> clazz = loader.loadClass(className);
} }
......
package emu.grasscutter.scripts;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.script.Bindings;
import javax.script.CompiledScript;
import javax.script.ScriptException;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.MonsterData;
import emu.grasscutter.data.def.WorldLevelData;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.constants.ScriptGadgetState;
import emu.grasscutter.scripts.constants.ScriptRegionShape;
import emu.grasscutter.scripts.data.SceneBlock;
import emu.grasscutter.scripts.data.SceneConfig;
import emu.grasscutter.scripts.data.SceneGadget;
import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.SceneInitConfig;
import emu.grasscutter.scripts.data.SceneMonster;
import emu.grasscutter.scripts.data.SceneSuite;
import emu.grasscutter.scripts.data.SceneTrigger;
import emu.grasscutter.scripts.data.SceneVar;
import emu.grasscutter.scripts.data.ScriptArgs;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public class SceneScriptManager {
private final Scene scene;
private final ScriptLib scriptLib;
private final LuaValue scriptLibLua;
private final Map<String, Integer> variables;
private Bindings bindings;
private SceneConfig config;
private List<SceneBlock> blocks;
private Int2ObjectOpenHashMap<Set<SceneTrigger>> triggers;
private boolean isInit;
public SceneScriptManager(Scene scene) {
this.scene = scene;
this.scriptLib = new ScriptLib(this);
this.scriptLibLua = CoerceJavaToLua.coerce(this.scriptLib);
this.triggers = new Int2ObjectOpenHashMap<>();
this.variables = new HashMap<>();
// TEMPORARY
if (this.getScene().getId() < 10) {
return;
}
// Create
this.init();
}
public Scene getScene() {
return scene;
}
public ScriptLib getScriptLib() {
return scriptLib;
}
public LuaValue getScriptLibLua() {
return scriptLibLua;
}
public Bindings getBindings() {
return bindings;
}
public SceneConfig getConfig() {
return config;
}
public List<SceneBlock> getBlocks() {
return blocks;
}
public Map<String, Integer> getVariables() {
return variables;
}
public Set<SceneTrigger> getTriggersByEvent(int eventId) {
return triggers.computeIfAbsent(eventId, e -> new HashSet<>());
}
public void registerTrigger(SceneTrigger trigger) {
getTriggersByEvent(trigger.event).add(trigger);
}
public void deregisterTrigger(SceneTrigger trigger) {
getTriggersByEvent(trigger.event).remove(trigger);
}
// TODO optimize
public SceneGroup getGroupById(int groupId) {
for (SceneBlock block : this.getScene().getLoadedBlocks()) {
for (SceneGroup group : block.groups) {
if (group.id == groupId) {
return group;
}
}
}
return null;
}
private void init() {
// Get compiled script if cached
CompiledScript cs = ScriptLoader.getScriptByPath(
Grasscutter.getConfig().SCRIPTS_FOLDER + "Scene/" + getScene().getId() + "/scene" + getScene().getId() + "." + ScriptLoader.getScriptType());
if (cs == null) {
Grasscutter.getLogger().warn("No script found for scene " + getScene().getId());
return;
}
// Create bindings
bindings = ScriptLoader.getEngine().createBindings();
// Set variables
bindings.put("EventType", new EventType()); // TODO - make static class to avoid instantiating a new class every scene
bindings.put("GadgetState", new ScriptGadgetState());
bindings.put("RegionShape", new ScriptRegionShape());
bindings.put("ScriptLib", getScriptLib());
// Eval script
try {
cs.eval(getBindings());
this.config = ScriptLoader.getSerializer().toObject(SceneConfig.class, bindings.get("scene_config"));
// TODO optimize later
// Create blocks
List<Integer> blockIds = ScriptLoader.getSerializer().toList(Integer.class, bindings.get("blocks"));
List<SceneBlock> blocks = ScriptLoader.getSerializer().toList(SceneBlock.class, bindings.get("block_rects"));
for (int i = 0; i < blocks.size(); i++) {
SceneBlock block = blocks.get(0);
block.id = blockIds.get(i);
loadBlockFromScript(block);
}
this.blocks = blocks;
} catch (ScriptException e) {
Grasscutter.getLogger().error("Error running script", e);
return;
}
// TEMP
this.isInit = true;
}
public boolean isInit() {
return isInit;
}
private void loadBlockFromScript(SceneBlock block) {
CompiledScript cs = ScriptLoader.getScriptByPath(
Grasscutter.getConfig().SCRIPTS_FOLDER + "Scene/" + getScene().getId() + "/scene" + getScene().getId() + "_block" + block.id + "." + ScriptLoader.getScriptType());
if (cs == null) {
return;
}
// Eval script
try {
cs.eval(getBindings());
// Set groups
block.groups = ScriptLoader.getSerializer().toList(SceneGroup.class, bindings.get("groups"));
block.groups.forEach(g -> g.block_id = block.id);
} catch (ScriptException e) {
Grasscutter.getLogger().error("Error loading block " + block.id + " in scene " + getScene().getId(), e);
}
}
public void loadGroupFromScript(SceneGroup group) {
// Set flag here so if there is no script, we dont call this function over and over again.
group.setLoaded(true);
CompiledScript cs = ScriptLoader.getScriptByPath(
Grasscutter.getConfig().SCRIPTS_FOLDER + "Scene/" + getScene().getId() + "/scene" + getScene().getId() + "_group" + group.id + "." + ScriptLoader.getScriptType());
if (cs == null) {
return;
}
// Eval script
try {
cs.eval(getBindings());
// Set
group.monsters = ScriptLoader.getSerializer().toList(SceneMonster.class, bindings.get("monsters"));
group.gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, bindings.get("gadgets"));
group.triggers = ScriptLoader.getSerializer().toList(SceneTrigger.class, bindings.get("triggers"));
group.suites = ScriptLoader.getSerializer().toList(SceneSuite.class, bindings.get("suites"));
group.init_config = ScriptLoader.getSerializer().toObject(SceneInitConfig.class, bindings.get("init_config"));
// Add variables to suite
List<SceneVar> variables = ScriptLoader.getSerializer().toList(SceneVar.class, bindings.get("variables"));
variables.forEach(var -> this.getVariables().put(var.name, var.value));
// Add monsters to suite TODO optimize
HashMap<Integer, SceneMonster> map = (HashMap<Integer, SceneMonster>) group.monsters.stream().collect(Collectors.toMap(m -> m.config_id, m -> m));
for (SceneSuite suite : group.suites) {
suite.sceneMonsters = new ArrayList<>(suite.monsters.size());
for (int id : suite.monsters) {
SceneMonster monster = map.get(id);
if (monster != null) {
suite.sceneMonsters.add(monster);
}
}
}
} catch (ScriptException e) {
Grasscutter.getLogger().error("Error loading group " + group.id + " in scene " + getScene().getId(), e);
}
}
public void onTick() {
checkTriggers();
}
public void checkTriggers() {
}
public void spawnGadgetsInGroup(SceneGroup group) {
for (SceneGadget g : group.gadgets) {
EntityGadget entity = new EntityGadget(getScene(), g.gadget_id, g.pos);
if (entity.getGadgetData() == null) continue;
entity.setBlockId(group.block_id);
entity.setConfigId(g.config_id);
entity.setGroupId(group.id);
entity.getRotation().set(g.rot);
entity.setState(g.state);
getScene().addEntity(entity);
this.callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(entity.getConfigId()));
}
}
public void spawnMonstersInGroup(SceneGroup group, int suiteIndex) {
spawnMonstersInGroup(group, group.getSuiteByIndex(suiteIndex));
}
public void spawnMonstersInGroup(SceneGroup group) {
spawnMonstersInGroup(group, null);
}
public void spawnMonstersInGroup(SceneGroup group, SceneSuite suite) {
List<SceneMonster> monsters = group.monsters;
if (suite != null) {
monsters = suite.sceneMonsters;
}
List<GameEntity> toAdd = new ArrayList<>();
for (SceneMonster monster : monsters) {
MonsterData data = GameData.getMonsterDataMap().get(monster.monster_id);
if (data == null) {
continue;
}
// Calculate level
int level = monster.level;
if (getScene().getDungeonData() != null) {
level = getScene().getDungeonData().getShowLevel();
} else if (getScene().getWorld().getWorldLevel() > 0) {
WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(getScene().getWorld().getWorldLevel());
if (worldLevelData != null) {
level = worldLevelData.getMonsterLevel();
}
}
// Spawn mob
EntityMonster entity = new EntityMonster(getScene(), data, monster.pos, level);
entity.getRotation().set(monster.rot);
entity.setGroupId(group.id);
entity.setConfigId(monster.config_id);
toAdd.add(entity);
}
if (toAdd.size() > 0) {
getScene().addEntities(toAdd);
for (GameEntity entity : toAdd) {
callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(entity.getConfigId()));
}
}
}
// Events
public void callEvent(int eventType, ScriptArgs params) {
for (SceneTrigger trigger : this.getTriggersByEvent(eventType)) {
LuaValue condition = null;
if (trigger.condition != null && !trigger.condition.isEmpty()) {
condition = (LuaValue) this.getBindings().get(trigger.condition);
}
LuaValue ret = LuaValue.TRUE;
if (condition != null) {
LuaValue args = LuaValue.NIL;
if (params != null) {
args = CoerceJavaToLua.coerce(params);
}
ret = condition.call(this.getScriptLibLua(), args);
}
if (ret.checkboolean() == true) {
LuaValue action = (LuaValue) this.getBindings().get(trigger.action);
action.call(this.getScriptLibLua(), LuaValue.NIL);
}
}
}
}
package emu.grasscutter.scripts;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.MonsterData;
import emu.grasscutter.game.dungeons.DungeonChallenge;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.SceneMonster;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketGadgetStateNotify;
import emu.grasscutter.server.packet.send.PacketWorktopOptionNotify;
public class ScriptLib {
private final SceneScriptManager sceneScriptManager;
public ScriptLib(SceneScriptManager sceneScriptManager) {
this.sceneScriptManager = sceneScriptManager;
}
public SceneScriptManager getSceneScriptManager() {
return sceneScriptManager;
}
public int SetGadgetStateByConfigId(int configId, int gadgetState) {
Optional<GameEntity> entity = getSceneScriptManager().getScene().getEntities().values().stream()
.filter(e -> e.getConfigId() == configId).findFirst();
if (entity.isEmpty()) {
return 1;
}
if (!(entity.get() instanceof EntityGadget)) {
return 1;
}
EntityGadget gadget = (EntityGadget) entity.get();
gadget.setState(gadgetState);
getSceneScriptManager().getScene().broadcastPacket(new PacketGadgetStateNotify(gadget, gadgetState));
return 0;
}
public int SetGroupGadgetStateByConfigId(int groupId, int configId, int gadgetState) {
List<GameEntity> list = getSceneScriptManager().getScene().getEntities().values().stream()
.filter(e -> e.getGroupId() == groupId).toList();
for (GameEntity entity : list) {
if (!(entity instanceof EntityGadget)) {
continue;
}
EntityGadget gadget = (EntityGadget) entity;
gadget.setState(gadgetState);
getSceneScriptManager().getScene().broadcastPacket(new PacketGadgetStateNotify(gadget, gadgetState));
}
return 0;
}
public int SetWorktopOptionsByGroupId(int groupId, int configId, int[] options) {
Optional<GameEntity> entity = getSceneScriptManager().getScene().getEntities().values().stream()
.filter(e -> e.getConfigId() == configId && e.getGroupId() == groupId).findFirst();
if (entity.isEmpty()) {
return 1;
}
if (!(entity.get() instanceof EntityGadget)) {
return 1;
}
EntityGadget gadget = (EntityGadget) entity.get();
gadget.addWorktopOptions(options);
getSceneScriptManager().getScene().broadcastPacket(new PacketWorktopOptionNotify(gadget));
return 0;
}
public int DelWorktopOptionByGroupId(int groupId, int configId, int option) {
Optional<GameEntity> entity = getSceneScriptManager().getScene().getEntities().values().stream()
.filter(e -> e.getConfigId() == configId && e.getGroupId() == groupId).findFirst();
if (entity.isEmpty()) {
return 1;
}
if (!(entity.get() instanceof EntityGadget)) {
return 1;
}
EntityGadget gadget = (EntityGadget) entity.get();
gadget.removeWorktopOption(option);
getSceneScriptManager().getScene().broadcastPacket(new PacketWorktopOptionNotify(gadget));
return 0;
}
// Some fields are guessed
public int AutoMonsterTide(int challengeIndex, int groupId, int[] config_ids, int param4, int param5, int param6) {
SceneGroup group = getSceneScriptManager().getGroupById(groupId);
if (group == null || group.monsters == null) {
return 1;
}
// TODO just spawn all from group for now
this.getSceneScriptManager().spawnMonstersInGroup(group);
return 0;
}
public int AddExtraGroupSuite(int groupId, int suite) {
SceneGroup group = getSceneScriptManager().getGroupById(groupId);
if (group == null || group.monsters == null) {
return 1;
}
// TODO just spawn all from group for now
this.getSceneScriptManager().spawnMonstersInGroup(group, suite);
return 0;
}
// param3 (probably time limit for timed dungeons)
public int ActiveChallenge(int challengeId, int challengeIndex, int param3, int groupId, int param4, int param5) {
SceneGroup group = getSceneScriptManager().getGroupById(groupId);
if (group == null || group.monsters == null) {
return 1;
}
DungeonChallenge challenge = new DungeonChallenge(getSceneScriptManager().getScene(), group);
challenge.setChallengeId(challengeId);
challenge.setChallengeIndex(challengeIndex);
getSceneScriptManager().getScene().setChallenge(challenge);
challenge.start();
return 0;
}
public int GetGroupMonsterCountByGroupId(int groupId) {
return (int) getSceneScriptManager().getScene().getEntities().values().stream()
.filter(e -> e instanceof EntityMonster && e.getGroupId() == groupId)
.count();
}
public int GetGroupVariableValue(String var) {
return getSceneScriptManager().getVariables().getOrDefault(var, 0);
}
public LuaValue ChangeGroupVariableValue(String var, int value) {
getSceneScriptManager().getVariables().put(var, value);
return LuaValue.ZERO;
}
public int RefreshGroup(LuaTable table) {
// Kill and Respawn?
int groupId = table.get("group_id").toint();
int suite = table.get("suite").toint();
SceneGroup group = getSceneScriptManager().getGroupById(groupId);
if (group == null || group.monsters == null) {
return 1;
}
// TODO just spawn all from group for now
this.getSceneScriptManager().spawnMonstersInGroup(group, suite);
return 0;
}
public void PrintContextLog(String msg) {
Grasscutter.getLogger().info("[LUA] " + msg);
}
}
package emu.grasscutter.scripts;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.scripts.serializer.LuaSerializer;
import emu.grasscutter.scripts.serializer.Serializer;
public class ScriptLoader {
private static ScriptEngineManager sm;
private static ScriptEngine engine;
private static ScriptEngineFactory factory;
private static String fileType;
private static Serializer serializer;
private static Map<String, CompiledScript> scripts = new HashMap<>();
public synchronized static void init() throws Exception {
if (sm != null) {
throw new Exception("Script loader already initialized");
}
sm = new ScriptEngineManager();
engine = sm.getEngineByName("luaj");
factory = getEngine().getFactory();
fileType = "lua";
serializer = new LuaSerializer();
}
public static ScriptEngine getEngine() {
return engine;
}
public static String getScriptType() {
return fileType;
}
public static Serializer getSerializer() {
return serializer;
}
public static CompiledScript getScriptByPath(String path) {
CompiledScript sc = scripts.get(path);
Grasscutter.getLogger().info("Loaded " + path);
if (sc == null) {
File file = new File(path);
if (!file.exists()) return null;
try (FileReader fr = new FileReader(file)) {
sc = ((Compilable) getEngine()).compile(fr);
scripts.put(path, sc);
} catch (Exception e) {
//e.printStackTrace();
return null;
}
}
return sc;
}
}
package emu.grasscutter.scripts.constants;
public class EventType {
public static final int EVENT_NONE = 0;
public static final int EVENT_ANY_MONSTER_DIE = 1;
public static final int EVENT_ANY_GADGET_DIE = 2;
public static final int EVENT_VARIABLE_CHANGE = 3;
public static final int EVENT_ENTER_REGION = 4;
public static final int EVENT_LEAVE_REGION = 5;
public static final int EVENT_GADGET_CREATE = 6;
public static final int EVENT_GADGET_STATE_CHANGE = 7;
public static final int EVENT_DUNGEON_SETTLE = 8;
public static final int EVENT_SELECT_OPTION = 9;
public static final int EVENT_CLIENT_EXECUTE = 10;
public static final int EVENT_ANY_MONSTER_LIVE = 11;
public static final int EVENT_SPECIFIC_MONSTER_HP_CHANGE = 12;
public static final int EVENT_CITY_LEVELUP_UNLOCK_DUNGEON_ENTRY = 13;
public static final int EVENT_DUNGEON_BROADCAST_ONTIMER = 14;
public static final int EVENT_TIMER_EVENT = 15;
public static final int EVENT_CHALLENGE_SUCCESS = 16;
public static final int EVENT_CHALLENGE_FAIL = 17;
public static final int EVENT_SEAL_BATTLE_BEGIN = 18;
public static final int EVENT_SEAL_BATTLE_END = 19;
public static final int EVENT_GATHER = 20;
public static final int EVENT_QUEST_FINISH = 21;
public static final int EVENT_MONSTER_BATTLE = 22;
public static final int EVENT_CITY_LEVELUP = 23;
public static final int EVENT_CUTSCENE_END = 24;
public static final int EVENT_AVATAR_NEAR_PLATFORM = 25;
public static final int EVENT_PLATFORM_REACH_POINT = 26;
public static final int EVENT_UNLOCK_TRANS_POINT = 27;
public static final int EVENT_QUEST_START = 28;
public static final int EVENT_GROUP_LOAD = 29;
public static final int EVENT_GROUP_WILL_UNLOAD = 30;
public static final int EVENT_GROUP_WILL_REFRESH = 31;
public static final int EVENT_GROUP_REFRESH = 32;
public static final int EVENT_DUNGEON_REWARD_GET = 33;
public static final int EVENT_SPECIFIC_GADGET_HP_CHANGE = 34;
public static final int EVENT_MONSTER_TIDE_OVER = 35;
public static final int EVENT_MONSTER_TIDE_CREATE = 36;
public static final int EVENT_MONSTER_TIDE_DIE = 37;
public static final int EVENT_SEALAMP_PHASE_CHANGE = 38;
public static final int EVENT_BLOSSOM_PROGRESS_FINISH = 39;
public static final int EVENT_BLOSSOM_CHEST_DIE = 40;
public static final int EVENT_GADGET_PLAY_START = 41;
public static final int EVENT_GADGET_PLAY_START_CD = 42;
public static final int EVENT_GADGET_PLAY_STOP = 43;
public static final int EVENT_GADGET_LUA_NOTIFY = 44;
public static final int EVENT_MP_PLAY_PREPARE = 45;
public static final int EVENT_MP_PLAY_BATTLE = 46;
public static final int EVENT_MP_PLAY_PREPARE_INTERRUPT = 47;
public static final int EVENT_SELECT_DIFFICULTY = 48;
public static final int EVENT_SCENE_MP_PLAY_BATTLE_STATE = 49;
public static final int EVENT_SCENE_MP_PLAY_BATTLE_STAGE_CHANGE = 50;
public static final int EVENT_SCENE_MP_PLAY_BATTLE_RESULT = 51;
public static final int EVENT_SEAL_BATTLE_PROGRESS_DECREASE = 52;
public static final int EVENT_GENERAL_REWARD_DIE = 53;
public static final int EVENT_SCENE_MP_PLAY_BATTLE_INTERRUPT = 54;
public static final int EVENT_MONSTER_DIE_BEFORE_LEAVE_SCENE = 55;
public static final int EVENT_SCENE_MP_PLAY_OPEN = 56;
public static final int EVENT_OFFERING_LEVELUP = 57;
public static final int EVENT_DUNGEON_REVIVE = 58;
public static final int EVENT_SCENE_MP_PLAY_ALL_AVATAR_DIE = 59;
public static final int EVENT_DUNGEON_ALL_AVATAR_DIE = 60;
public static final int EVENT_GENERAL_REWARD_TAKEN = 61;
public static final int EVENT_PLATFORM_REACH_ARRAYPOINT = 62;
public static final int EVENT_SCENE_MULTISTAGE_PLAY_STAGE_END = 63;
public static final int EVENT_SCENE_MULTISTAGE_PLAY_END_STAGE_REQ = 64;
public static final int EVENT_MECHANICUS_PICKED_CARD = 65;
public static final int EVENT_POOL_MONSTER_TIDE_OVER = 66;
public static final int EVENT_POOL_MONSTER_TIDE_CREATE = 67;
public static final int EVENT_POOL_MONSTER_TIDE_DIE = 68;
public static final int EVENT_DUNGEON_AVATAR_SLIP_DIE = 69;
public static final int EVENT_GALLERY_START = 70;
public static final int EVENT_GALLERY_STOP = 71;
public static final int EVENT_TIME_AXIS_PASS = 72;
public static final int EVENT_FLEUR_FAIR_DUNGEON_ALL_PLAYER_ENTER = 73;
public static final int EVENT_GADGETTALK_DONE = 74;
public static final int EVENT_SET_GAME_TIME = 75;
public static final int EVENT_HIDE_AND_SEEK_PLAYER_QUIT = 76;
public static final int EVENT_AVATAR_DIE = 77;
}
package emu.grasscutter.scripts.constants;
public class ScriptGadgetState {
public static final int Default = 0;
public static final int GatherDrop = 1;
public static final int ChestLocked = 101;
public static final int ChestOpened = 102;
public static final int ChestTrap = 103;
public static final int ChestBramble = 104;
public static final int ChestFrozen = 105;
public static final int ChestRock = 106;
public static final int GearStart = 201;
public static final int GearStop = 202;
public static final int GearAction1 = 203;
public static final int GearAction2 = 204;
public static final int CrystalResonate1 = 301;
public static final int CrystalResonate2 = 302;
public static final int CrystalExplode = 303;
public static final int CrystalDrain = 304;
public static final int StatueActive = 401;
public static final int Action01 = 901;
public static final int Action02 = 902;
public static final int Action03 = 903;
}
package emu.grasscutter.scripts.constants;
public class ScriptRegionShape {
public static final int NONE = 0;
public static final int SPHERE = 1;
public static final int CUBIC = 2;
}
package emu.grasscutter.scripts.data;
import java.util.List;
import emu.grasscutter.utils.Position;
public class SceneBlock {
public int id;
public Position max;
public Position min;
public List<SceneGroup> groups;
public boolean contains(Position pos) {
return pos.getX() <= max.getX() && pos.getX() >= min.getX() &&
pos.getZ() <= max.getZ() && pos.getZ() >= min.getZ();
}
}
package emu.grasscutter.scripts.data;
import emu.grasscutter.utils.Position;
public class SceneConfig {
public Position vision_anchor;
public Position born_pos;
public Position born_rot;
public Position begin_pos;
public Position size;
}
package emu.grasscutter.scripts.data;
import emu.grasscutter.utils.Position;
public class SceneGadget {
public int level;
public int config_id;
public int gadget_id;
public int state;
public Position pos;
public Position rot;
}
package emu.grasscutter.scripts.data;
import java.util.List;
import emu.grasscutter.utils.Position;
public class SceneGroup {
public transient int block_id; // Not an actual variable in the scripts but we will keep it here for reference
public int id;
public int refresh_id;
public Position pos;
public List<SceneMonster> monsters;
public List<SceneGadget> gadgets;
public List<SceneTrigger> triggers;
public List<SceneSuite> suites;
public SceneInitConfig init_config;
private transient boolean isLoaded; // Not an actual variable in the scripts either
public boolean isLoaded() {
return isLoaded;
}
public boolean setLoaded(boolean loaded) {
return loaded;
}
public SceneSuite getSuiteByIndex(int index) {
return suites.get(index - 1);
}
}
package emu.grasscutter.scripts.data;
import emu.grasscutter.utils.Position;
public class SceneInitConfig {
public int suite;
public int end_suite;
public int rand_suite;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment