Framework for array points (#156)
Some checks are pending
Build / Build-Server-Jar (push) Waiting to run

This commit is contained in:
Nazrin 2024-10-20 20:04:38 -07:00 committed by GitHub
parent 12b08903ae
commit 9138b96a94
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 185 additions and 71 deletions

View File

@ -1,45 +1,23 @@
package emu.grasscutter.data;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.binout.*;
import emu.grasscutter.data.binout.config.ConfigEntityAvatar;
import emu.grasscutter.data.binout.config.ConfigEntityGadget;
import emu.grasscutter.data.binout.config.ConfigEntityMonster;
import emu.grasscutter.data.binout.config.ConfigGlobalCombat;
import emu.grasscutter.data.binout.config.ConfigLevelEntity;
import emu.grasscutter.game.dungeons.dungeon_entry.DungeonEntries;
import emu.grasscutter.data.binout.config.*;
import emu.grasscutter.data.binout.routes.Route;
import emu.grasscutter.data.common.ScenePointArrayData;
import emu.grasscutter.data.common.WeatherAreaPointData;
import emu.grasscutter.data.common.quest.MainQuestData;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.binout.routes.Route;
import emu.grasscutter.data.custom.*;
import emu.grasscutter.data.server.ActivityCondGroup;
import emu.grasscutter.data.server.DropSubfieldMapping;
import emu.grasscutter.data.server.DropTableExcelConfigData;
import emu.grasscutter.data.server.GadgetMapping;
import emu.grasscutter.data.server.MonsterMapping;
import emu.grasscutter.data.server.SubfieldMapping;
import emu.grasscutter.data.server.WeatherMapping;
import emu.grasscutter.data.custom.TrialAvatarActivityCustomData;
import emu.grasscutter.data.custom.TrialAvatarCustomData;
import emu.grasscutter.data.excels.*;
import emu.grasscutter.data.server.*;
import emu.grasscutter.game.dungeons.DungeonDropEntry;
import emu.grasscutter.game.dungeons.dungeon_entry.DungeonEntries;
import emu.grasscutter.game.quest.QuestEncryptionKey;
import emu.grasscutter.game.quest.enums.QuestCond;
import emu.grasscutter.utils.Utils;
import emu.grasscutter.data.excels.*;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.ints.*;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Tolerate;
@ -50,6 +28,8 @@ import org.anime_game_servers.gi_lua.models.scene.DummyPoint;
import org.anime_game_servers.gi_lua.models.scene.SceneGroupReplacement;
import javax.annotation.Nullable;
import java.lang.reflect.Field;
import java.util.*;
public class GameData {
// BinOutputs
@ -219,6 +199,7 @@ public class GameData {
@Getter private static final Map<Integer, TrialAvatarActivityDataData> trialAvatarActivityDataCustomData = new HashMap<>();
@Getter private static final Int2IntMap trialAvatarIndexIdTrialActivityDataDataMap = new Int2IntOpenHashMap();
@Getter private static final Map<Integer, List<WeatherAreaPointData>> weatherAreaPointData = new HashMap<>();
@Getter private static final Map<Integer, List<ScenePointArrayData>> scenePointArrayData = new HashMap<>();
// Getters with wrong names, remove later
@Deprecated(forRemoval = true) public static Int2ObjectMap<CodexReliquaryData> getcodexReliquaryIdMap() {return codexReliquaryDataIdMap;}
@ -336,6 +317,10 @@ public class GameData {
return sceneRouteData.computeIfAbsent(sceneId, k -> new Int2ObjectOpenHashMap<>());
}
public static List<ScenePointArrayData> getScenePointArrays(int sceneId) {
return scenePointArrayData.computeIfAbsent(sceneId, k -> List.of(new ScenePointArrayData()));
}
@Nullable
public static TrialAvatarActivityDataData getTrialAvatarActivityDataByAvatarIndex(int trialAvatarIndexId){
// prefer custom data over official data

View File

@ -2,29 +2,24 @@ package emu.grasscutter.data;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Loggers;
import emu.grasscutter.data.binout.*;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
import emu.grasscutter.data.binout.config.*;
import emu.grasscutter.data.binout.config.fields.ConfigAbilityData;
import emu.grasscutter.game.dungeons.dungeon_entry.DungeonEntries;
import emu.grasscutter.data.common.quest.MainQuestData;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.binout.routes.SceneRoutes;
import emu.grasscutter.data.common.PointData;
import emu.grasscutter.data.common.ScenePointArrayData;
import emu.grasscutter.data.common.WeatherAreaPointData;
import emu.grasscutter.data.custom.*;
import emu.grasscutter.data.common.quest.MainQuestData;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.custom.TrialAvatarActivityCustomData;
import emu.grasscutter.data.custom.TrialAvatarCustomData;
import emu.grasscutter.data.excels.TrialAvatarActivityDataData;
import emu.grasscutter.data.server.ActivityCondGroup;
import emu.grasscutter.data.server.DropSubfieldMapping;
import emu.grasscutter.data.server.DropTableExcelConfigData;
import emu.grasscutter.data.server.GadgetMapping;
import emu.grasscutter.data.server.MonsterMapping;
import emu.grasscutter.data.server.SubfieldMapping;
import emu.grasscutter.data.server.WeatherMapping;
import emu.grasscutter.data.server.*;
import emu.grasscutter.game.ability.Ability;
import emu.grasscutter.game.dungeons.DungeonDrop;
import emu.grasscutter.game.dungeons.dungeon_entry.DungeonEntries;
import emu.grasscutter.game.dungeons.enums.DungeonType;
import emu.grasscutter.game.managers.blossom.BlossomConfig;
import emu.grasscutter.game.quest.QuestEncryptionKey;
@ -33,7 +28,9 @@ import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.game.world.SpawnDataEntry;
import emu.grasscutter.game.world.SpawnDataEntry.GridBlockId;
import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry;
import emu.grasscutter.scripts.*;
import emu.grasscutter.scripts.EntityControllerScriptManager;
import emu.grasscutter.scripts.SceneIndexManager;
import emu.grasscutter.scripts.ScriptSystem;
import emu.grasscutter.utils.FileUtils;
import emu.grasscutter.utils.JsonUtils;
import emu.grasscutter.utils.TsvUtils;
@ -42,7 +39,6 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntArraySet;
import lombok.val;
import org.anime_game_servers.gi_lua.models.loader.SceneReplacementScriptLoadParams;
import org.anime_game_servers.gi_lua.models.loader.ShardQuestScriptLoadParams;
import org.anime_game_servers.gi_lua.models.quest.QuestData;
@ -51,17 +47,18 @@ import org.anime_game_servers.gi_lua.models.scene.SceneGroupReplacement;
import org.reflections.Reflections;
import org.slf4j.Logger;
import java.io.*;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import java.util.stream.Collectors;
import java.util.Map.Entry;
import java.util.stream.Stream;
import static emu.grasscutter.utils.FileUtils.getDataPath;
import static emu.grasscutter.utils.FileUtils.getResourcePath;
@ -126,6 +123,7 @@ public class ResourceLoader {
GameDepot.load();
// Load spawn data and quests
loadSceneRoutes();
loadScenePointArrays();
loadSpawnData();
loadQuests();
loadScriptSceneData();
@ -296,7 +294,7 @@ public class ResourceLoader {
});
});
} catch (IOException e) {
logger.error("Weather area files cannot be found");
logger.error("Weather area files cannot be found in {}", dirPath);
}
});
} catch (IOException e) {
@ -749,6 +747,41 @@ public class ResourceLoader {
}
}
private static void loadScenePointArrays() {
val pattern = Pattern.compile("scene([0-9]+)_point_array\\.json");
try (val dirStream = Files.newDirectoryStream(getResourcePath("Scripts/Scene"))) {
dirStream.forEach(dirPath -> {
try (val stream = Files.newDirectoryStream(dirPath, "scene*_point_array.json")) {
stream.forEach(path -> {
val matcher = pattern.matcher(path.getFileName().toString());
if (!matcher.find()) return;
int sceneId = Integer.parseInt(matcher.group(1));
List<ScenePointArrayData> config;
try {
config = JsonUtils.loadToList(path, ScenePointArrayData.class);
} catch (Exception e) {
logger.error("Error loading point array file: {}", path, e);
return;
}
if (config == null) return;
config.forEach((pointArray) -> {
GameData.getScenePointArrayData().put(sceneId, config);
});
});
} catch (IOException e) {
logger.error("Point array files cannot be found in {}", dirPath);
}
});
} catch (IOException e) {
logger.error("Point array files cannot be found");
}
}
private static void loadBlossomResources() {
try {
GameDepot.setBlossomConfig(DataLoader.loadClass("BlossomConfig.json", BlossomConfig.class));

View File

@ -5,8 +5,8 @@ import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
import lombok.val;
import org.anime_game_servers.multi_proto.gi.messages.general.RoutePoint.MoveParams;
import org.anime_game_servers.multi_proto.gi.messages.general.RoutePoint.MoveParams.Time;
import org.anime_game_servers.multi_proto.gi.messages.general.RoutePoint.MoveParams.Velocity;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
@ -23,9 +23,9 @@ public class RoutePoint {
public org.anime_game_servers.multi_proto.gi.messages.general.RoutePoint toProto(){
val proto = new org.anime_game_servers.multi_proto.gi.messages.general.RoutePoint(pos.toProto());
if(waitTime!=0){
proto.setMoveParams(new MoveParams.Time(waitTime));
proto.setMoveParams(new Time(waitTime));
} else if(targetVelocity!=0){
proto.setMoveParams(new MoveParams.Velocity(targetVelocity));
proto.setMoveParams(new Velocity(targetVelocity));
}
return proto;

View File

@ -0,0 +1,50 @@
package emu.grasscutter.data.common;
import emu.grasscutter.utils.Position;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.val;
import org.anime_game_servers.multi_proto.gi.messages.general.RoutePoint;
@Data
@EqualsAndHashCode(callSuper = false)
@SuppressWarnings(value = "SpellCheckingInspection")
public class ScenePointArrayData {
public int pointArrayId;
public boolean isSimulateTempRouteByTime = false;
public boolean isMoveToRoutePointOnInit = false;
public PointArrayPoint[] platformPointList;
@Data
@EqualsAndHashCode(callSuper = false)
@SuppressWarnings(value = "SpellCheckingInspection")
public class PointArrayPoint {
public int pointId;
public Position position;
public Position rotation;
public float velocity;
public float time;
public boolean isReachEvent;
public Position rotAxis;
public float rotSpeed;
public float arriveRange;
public RoutePoint toProto() {
val proto = new RoutePoint();
proto.setArriveRange(arriveRange);
proto.setPosition(position.toProto());
if (rotSpeed == 0.0f) {
proto.setRotateParams(new RoutePoint.RotateParams.Rotation(rotation.toProto()));
}
if (velocity > time) {
proto.setMoveParams(new RoutePoint.MoveParams.Velocity(velocity));
} else {
proto.setMoveParams(new RoutePoint.MoveParams.Time(time));
}
return proto;
}
}
}

View File

@ -9,6 +9,7 @@ import emu.grasscutter.game.ability.AbilityManager;
import emu.grasscutter.game.entity.gadget.*;
import emu.grasscutter.game.entity.gadget.platform.BaseRoute;
import emu.grasscutter.game.entity.gadget.platform.ConfigRoute;
import emu.grasscutter.game.entity.gadget.platform.PointArrayRoute;
import emu.grasscutter.game.entity.interfaces.ConfigAbilityDataAbilityEntity;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityIdType;
@ -37,9 +38,7 @@ import org.anime_game_servers.multi_proto.gi.messages.scene.VisionType;
import org.anime_game_servers.multi_proto.gi.messages.scene.entity.*;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.*;
@ToString(callSuper = true)
public class EntityGadget extends EntityBaseGadget implements ConfigAbilityDataAbilityEntity {
@ -160,7 +159,7 @@ public class EntityGadget extends EntityBaseGadget implements ConfigAbilityDataA
case Worktop, SealGadget -> new GadgetWorktop(this);
case RewardStatue -> new GadgetRewardStatue(this);
case Chest -> new GadgetChest(this);
case Gadget -> new GadgetObject(this);
case Gadget, Platform -> new GadgetObject(this);
case Screen -> new GadgetScreen(this);
case ViewPoint -> new GadgetViewPoint(this);
default -> null;
@ -172,6 +171,8 @@ public class EntityGadget extends EntityBaseGadget implements ConfigAbilityDataA
if(!interactEnabled) return;
if (this.getContent() == null) {
val contentName = GameData.getGadgetDataMap().get(interactReq.getGadgetId()).getType().name();
Grasscutter.getLogger().warn("Missing Gadget content: {}", contentName);
return;
}
@ -248,6 +249,33 @@ public class EntityGadget extends EntityBaseGadget implements ConfigAbilityDataA
return true;
}
public boolean scheduleArrayPoints(int pointArrayId, List<Integer> platformPointList) {
if (platformPointList.size() > 1)
Grasscutter.getLogger().warn("Untested PointArrayRoute with multiple points! Keep an eye on Route {} at pos: {}", pointArrayId, bornPos);
if (!(routeConfig instanceof PointArrayRoute pointArrayRoute)) {
Grasscutter.getLogger().error("routeConfig not instance of PointArrayRoute");
return false;
}
var route = this.getScene().getPointArrayById(pointArrayId);
if (route == null) {
Grasscutter.getLogger().error("Cannot find route with ID {}", pointArrayId);
return false;
}
var points = route.getPlatformPointList();
val routePointList = platformPointList.stream().map(x -> Arrays.stream(points).filter(y -> y.getPointId() == x).findFirst().orElse(null).toProto()).toList();
pointArrayRoute.setRoutePoints(routePointList);
pointArrayRoute.startRoute(getScene());
pointArrayRoute.setStartSceneTime(getScene().getSceneTime() + 2000); //todo: read routePointList.get(0).getMoveParams() for this offest
return true;
}
public boolean schedulePlatform() {
if (!(routeConfig instanceof ConfigRoute configRoute)) {
return false;

View File

@ -4,31 +4,39 @@ import emu.grasscutter.utils.Position;
import lombok.Getter;
import lombok.Setter;
import lombok.val;
import org.anime_game_servers.gi_lua.models.scene.group.SceneGadget;
import org.anime_game_servers.multi_proto.gi.messages.general.RoutePoint;
import org.anime_game_servers.multi_proto.gi.messages.scene.entity.MovingPlatformType;
import org.anime_game_servers.multi_proto.gi.messages.scene.entity.PlatformInfo;
import org.anime_game_servers.gi_lua.models.scene.group.SceneGadget;
import org.anime_game_servers.multi_proto.gi.messages.scene.entity.Route;
import java.util.ArrayList;
import java.util.List;
/**
* TODO implement point array routes, read from missing resources
*/
public class PointArrayRoute extends BaseRoute {
@Getter @Setter int currentPoint;
@Getter @Setter int pointArrayId;
@Getter @Setter List<RoutePoint> routePoints;
public PointArrayRoute(SceneGadget gadget) {
super(gadget);
routePoints = new ArrayList<>();
}
public PointArrayRoute(Position startRot, boolean startRoute, boolean isActive, int pointArrayId) {
super(startRot, startRoute, isActive);
this.pointArrayId = pointArrayId;
routePoints = new ArrayList<>();
}
@Override
public PlatformInfo toProto() {
val proto = super.toProto();
proto.setMovingPlatformType(MovingPlatformType.MOVING_PLATFORM_ROUTE);
val routeInfo = new Route();
routeInfo.setRoutePoints(routePoints);
proto.setRoute(routeInfo);
return proto;
}
}

View File

@ -6,6 +6,7 @@ import emu.grasscutter.data.GameDepot;
import emu.grasscutter.data.binout.SceneNpcBornEntry;
import emu.grasscutter.data.binout.routes.Route;
import emu.grasscutter.data.binout.routes.RouteType;
import emu.grasscutter.data.common.ScenePointArrayData;
import emu.grasscutter.data.excels.CodexAnimalData;
import emu.grasscutter.data.excels.DungeonData;
import emu.grasscutter.data.excels.SceneData;
@ -75,6 +76,7 @@ public class Scene {
private final long startWorldTime;
@Getter @Setter DungeonManager dungeonManager;
@Getter Int2ObjectMap<Route> sceneRoutes;
@Getter List<ScenePointArrayData> pointArrays;
private Set<SpawnDataEntry.GridBlockId> loadedGridBlocks = new HashSet<>();
@Getter @Setter private boolean dontDestroyWhenEmpty;
@Getter private final SceneScriptManager scriptManager;
@ -106,6 +108,7 @@ public class Scene {
this.prevScene = 3;
this.sceneRoutes = GameData.getSceneRoutes(getId());
this.pointArrays = GameData.getScenePointArrays(getId());
this.startWorldTime = world.getWorldTime();
@ -162,6 +165,11 @@ public class Scene {
return this.sceneRoutes.get(routeId);
}
@Nullable
public ScenePointArrayData getPointArrayById(int routeId) {
return this.pointArrays.stream().filter(x -> x.getPointArrayId() == routeId).findFirst().orElse(null);
}
public void setPaused(boolean paused) {
if (this.isPaused != paused) {
this.isPaused = paused;

View File

@ -7,24 +7,24 @@ import emu.grasscutter.game.dungeons.challenge.ChallengeInfo;
import emu.grasscutter.game.dungeons.challenge.ChallengeScoreInfo;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.challenge.factory.ChallengeFactory;
import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.entity.gadget.GadgetWorktop;
import emu.grasscutter.game.entity.gadget.platform.ConfigRoute;
import emu.grasscutter.game.entity.gadget.platform.PointArrayRoute;
import emu.grasscutter.game.managers.blossom.BlossomSchedule;
import emu.grasscutter.game.managers.blossom.enums.BlossomRefreshType;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ClimateType;
import emu.grasscutter.game.quest.enums.QuestCond;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.game.world.SceneGroupInstance;
import emu.grasscutter.game.world.WeatherArea;
import emu.grasscutter.scripts.lua_engine.GroupEventLuaContext;
import emu.grasscutter.scripts.scriptlib_handlers.BaseHandler;
import emu.grasscutter.server.packet.send.*;
import lombok.Getter;
import lombok.val;
import org.anime_game_servers.multi_proto.gi.messages.scene.EnterType;
import org.anime_game_servers.core.gi.enums.QuestState;
import org.anime_game_servers.core.gi.models.Vector;
import org.anime_game_servers.gi_lua.models.ScriptArgs;
@ -38,14 +38,15 @@ import org.anime_game_servers.gi_lua.script_lib.LuaContext;
import org.anime_game_servers.gi_lua.script_lib.SealBattleParams;
import org.anime_game_servers.gi_lua.script_lib.handler.parameter.KillByConfigIdParams;
import org.anime_game_servers.lua.engine.LuaTable;
import org.anime_game_servers.multi_proto.gi.messages.scene.EnterType;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import java.util.*;
import static emu.grasscutter.game.props.EnterReason.Lua;
import static org.anime_game_servers.gi_lua.utils.ScriptUtils.luaToPos;
import static org.anime_game_servers.gi_lua.models.constants.GroupKillPolicy.*;
import static org.anime_game_servers.gi_lua.utils.ScriptUtils.luaToPos;
public class ScriptLibHandler extends BaseHandler implements org.anime_game_servers.gi_lua.script_lib.ScriptLibHandler<GroupEventLuaContext> {
@Getter
@ -1456,7 +1457,7 @@ public class ScriptLibHandler extends BaseHandler implements org.anime_game_serv
*/
@Override
public int SetPlatformPointArray(GroupEventLuaContext context, int entityConfigId, int pointArrayId, LuaTable var3, LuaTable var4) {
logger.warn("[LUA] Call unimplemented SetPlatformPointArray with {} {} {} {}", entityConfigId, pointArrayId, printTable(var3), printTable(var4));
logger.warn("[LUA] Call half implemented SetPlatformPointArray with {} {} {} {}", entityConfigId, pointArrayId, printTable(var3), printTable(var4));
val groupId = context.getCurrentGroup().getGroupInfo().getId();
val scene = context.getSceneScriptManager().getScene();
@ -1475,16 +1476,17 @@ public class ScriptLibHandler extends BaseHandler implements org.anime_game_serv
}
val configRoute = (PointArrayRoute) routeConfig;
//TODO also check targetPoint/targetPoints
if(configRoute.getPointArrayId() == pointArrayId){
return -1;
}
configRoute.setPointArrayId(pointArrayId);
//TODO also set targetPoint/targetPoints
val pointIndexList = Arrays.stream(var3.getAsIntArray()).boxed().toList();
if (!entityGadget.scheduleArrayPoints(pointArrayId, pointIndexList)) {
return -1;
}
context.getSceneScriptManager().getScene().broadcastPacket(new PacketPlatformChangeRouteNotify(entityGadget));
return -1;
return 0;
}
@Override