mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-02-25 03:52:55 +08:00
Merge branch 'dev-world-scripts' of https://github.com/Grasscutters/Grasscutter into development
This commit is contained in:
commit
30c7bb9443
@ -86,6 +86,9 @@ dependencies {
|
|||||||
|
|
||||||
implementation group: 'org.luaj', name: 'luaj-jse', version: '3.0.1'
|
implementation group: 'org.luaj', name: 'luaj-jse', version: '3.0.1'
|
||||||
|
|
||||||
|
implementation group: 'com.esotericsoftware', name : 'reflectasm', version: '1.11.9'
|
||||||
|
implementation group: 'com.github.davidmoten', name : 'rtree-multi', version: '0.1'
|
||||||
|
|
||||||
protobuf files('proto/')
|
protobuf files('proto/')
|
||||||
|
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.24'
|
compileOnly 'org.projectlombok:lombok:1.18.24'
|
||||||
|
@ -24,8 +24,8 @@ public class GameData {
|
|||||||
private static final Map<String, OpenConfigEntry> openConfigEntries = new HashMap<>();
|
private static final Map<String, OpenConfigEntry> openConfigEntries = new HashMap<>();
|
||||||
private static final Map<String, ScenePointEntry> scenePointEntries = new HashMap<>();
|
private static final Map<String, ScenePointEntry> scenePointEntries = new HashMap<>();
|
||||||
private static final Int2ObjectMap<MainQuestData> mainQuestData = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<MainQuestData> mainQuestData = new Int2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
private static final Int2ObjectMap<HomeworldDefaultSaveData> homeworldDefaultSaveData = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<HomeworldDefaultSaveData> homeworldDefaultSaveData = new Int2ObjectOpenHashMap<>();
|
||||||
|
private static final Int2ObjectMap<SceneNpcBornData> npcBornData = new Int2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
// ExcelConfigs
|
// ExcelConfigs
|
||||||
private static final Int2ObjectMap<PlayerLevelData> playerLevelDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<PlayerLevelData> playerLevelDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
@ -83,12 +83,14 @@ public class GameData {
|
|||||||
private static final Int2ObjectMap<ShopGoodsData> shopGoodsDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<ShopGoodsData> shopGoodsDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<CombineData> combineDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<CombineData> combineDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<RewardPreviewData> rewardPreviewDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<RewardPreviewData> rewardPreviewDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
private static final Int2ObjectMap<GatherData> gatherDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<TowerFloorData> towerFloorDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<TowerFloorData> towerFloorDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<TowerLevelData> towerLevelDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<TowerLevelData> towerLevelDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<TowerScheduleData> towerScheduleDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<TowerScheduleData> towerScheduleDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<ForgeData> forgeDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<ForgeData> forgeDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<HomeWorldLevelData> homeWorldLevelDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<HomeWorldLevelData> homeWorldLevelDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<FurnitureMakeConfigData> furnitureMakeConfigDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<FurnitureMakeConfigData> furnitureMakeConfigDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
private static final Int2ObjectMap<InvestigationMonsterData> investigationMonsterDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
// Cache
|
// Cache
|
||||||
private static Map<Integer, List<Integer>> fetters = new HashMap<>();
|
private static Map<Integer, List<Integer>> fetters = new HashMap<>();
|
||||||
@ -140,9 +142,15 @@ public class GameData {
|
|||||||
public static Int2ObjectMap<MainQuestData> getMainQuestDataMap() {
|
public static Int2ObjectMap<MainQuestData> getMainQuestDataMap() {
|
||||||
return mainQuestData;
|
return mainQuestData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Int2ObjectMap<HomeworldDefaultSaveData> getHomeworldDefaultSaveData() {
|
public static Int2ObjectMap<HomeworldDefaultSaveData> getHomeworldDefaultSaveData() {
|
||||||
return homeworldDefaultSaveData;
|
return homeworldDefaultSaveData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Int2ObjectMap<SceneNpcBornData> getSceneNpcBornData() {
|
||||||
|
return npcBornData;
|
||||||
|
}
|
||||||
|
|
||||||
public static Int2ObjectMap<AvatarData> getAvatarDataMap() {
|
public static Int2ObjectMap<AvatarData> getAvatarDataMap() {
|
||||||
return avatarDataMap;
|
return avatarDataMap;
|
||||||
}
|
}
|
||||||
@ -365,9 +373,11 @@ public class GameData {
|
|||||||
public static Int2ObjectMap<TowerFloorData> getTowerFloorDataMap(){
|
public static Int2ObjectMap<TowerFloorData> getTowerFloorDataMap(){
|
||||||
return towerFloorDataMap;
|
return towerFloorDataMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Int2ObjectMap<TowerLevelData> getTowerLevelDataMap(){
|
public static Int2ObjectMap<TowerLevelData> getTowerLevelDataMap(){
|
||||||
return towerLevelDataMap;
|
return towerLevelDataMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Int2ObjectMap<TowerScheduleData> getTowerScheduleDataMap(){
|
public static Int2ObjectMap<TowerScheduleData> getTowerScheduleDataMap(){
|
||||||
return towerScheduleDataMap;
|
return towerScheduleDataMap;
|
||||||
}
|
}
|
||||||
@ -379,10 +389,20 @@ public class GameData {
|
|||||||
public static Int2ObjectMap<ForgeData> getForgeDataMap() {
|
public static Int2ObjectMap<ForgeData> getForgeDataMap() {
|
||||||
return forgeDataMap;
|
return forgeDataMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Int2ObjectMap<HomeWorldLevelData> getHomeWorldLevelDataMap() {
|
public static Int2ObjectMap<HomeWorldLevelData> getHomeWorldLevelDataMap() {
|
||||||
return homeWorldLevelDataMap;
|
return homeWorldLevelDataMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Int2ObjectMap<FurnitureMakeConfigData> getFurnitureMakeConfigDataMap() {
|
public static Int2ObjectMap<FurnitureMakeConfigData> getFurnitureMakeConfigDataMap() {
|
||||||
return furnitureMakeConfigDataMap;
|
return furnitureMakeConfigDataMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Int2ObjectMap<GatherData> getGatherDataMap() {
|
||||||
|
return gatherDataMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Int2ObjectMap<InvestigationMonsterData> getInvestigationMonsterDataMap() {
|
||||||
|
return investigationMonsterDataMap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import emu.grasscutter.data.binout.*;
|
import emu.grasscutter.data.binout.*;
|
||||||
|
import emu.grasscutter.scripts.SceneIndexManager;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import org.reflections.Reflections;
|
import org.reflections.Reflections;
|
||||||
@ -64,10 +65,9 @@ public class ResourceLoader {
|
|||||||
loadQuests();
|
loadQuests();
|
||||||
// Load scene points - must be done AFTER resources are loaded
|
// Load scene points - must be done AFTER resources are loaded
|
||||||
loadScenePoints();
|
loadScenePoints();
|
||||||
|
|
||||||
// Load default home layout
|
// Load default home layout
|
||||||
loadHomeworldDefaultSaveData();
|
loadHomeworldDefaultSaveData();
|
||||||
|
loadNpcBornData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void loadResources() {
|
public static void loadResources() {
|
||||||
@ -416,6 +416,27 @@ public class ResourceLoader {
|
|||||||
Grasscutter.getLogger().info("Loaded " + GameData.getHomeworldDefaultSaveData().size() + " HomeworldDefaultSaveDatas.");
|
Grasscutter.getLogger().info("Loaded " + GameData.getHomeworldDefaultSaveData().size() + " HomeworldDefaultSaveDatas.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
private static void loadNpcBornData(){
|
||||||
|
var folder = Files.list(Path.of(RESOURCE("BinOutput/Scene/SceneNpcBorn"))).toList();
|
||||||
|
|
||||||
|
for(var file : folder){
|
||||||
|
if(file.toFile().isDirectory()){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = Grasscutter.getGsonFactory().fromJson(Files.readString(file), SceneNpcBornData.class);
|
||||||
|
if(data.getBornPosList() == null || data.getBornPosList().size() == 0){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.setIndex(SceneIndexManager.buildIndex(3, data.getBornPosList(), item -> item.getPos().toPoint()));
|
||||||
|
GameData.getSceneNpcBornData().put(data.getSceneId(), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Grasscutter.getLogger().info("Loaded " + GameData.getSceneNpcBornData().size() + " SceneNpcBornDatas.");
|
||||||
|
}
|
||||||
|
|
||||||
// BinOutput configs
|
// BinOutput configs
|
||||||
|
|
||||||
public static class AvatarConfig {
|
public static class AvatarConfig {
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
package emu.grasscutter.data.custom;
|
||||||
|
|
||||||
|
import com.github.davidmoten.rtreemulti.RTree;
|
||||||
|
import com.github.davidmoten.rtreemulti.geometry.Geometry;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.FieldDefaults;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
|
public class SceneNpcBornData {
|
||||||
|
int sceneId;
|
||||||
|
List<SceneNpcBornEntry> bornPosList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spatial Index For NPC
|
||||||
|
*/
|
||||||
|
transient RTree<SceneNpcBornEntry, Geometry> index;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* npc groups
|
||||||
|
*/
|
||||||
|
transient Map<Integer, SceneGroup> groups = new ConcurrentHashMap<>();
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package emu.grasscutter.data.custom;
|
||||||
|
|
||||||
|
import emu.grasscutter.utils.Position;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.FieldDefaults;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
|
public class SceneNpcBornEntry {
|
||||||
|
int id;
|
||||||
|
int configId;
|
||||||
|
Position pos;
|
||||||
|
Position rot;
|
||||||
|
int groupId;
|
||||||
|
List<Integer> suiteIdList;
|
||||||
|
}
|
49
src/main/java/emu/grasscutter/data/excels/GatherData.java
Normal file
49
src/main/java/emu/grasscutter/data/excels/GatherData.java
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package emu.grasscutter.data.def;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.GameResource;
|
||||||
|
import emu.grasscutter.data.ResourceType;
|
||||||
|
|
||||||
|
@ResourceType(name = "GatherExcelConfigData.json")
|
||||||
|
public class GatherData extends GameResource {
|
||||||
|
private int PointType;
|
||||||
|
private int Id;
|
||||||
|
private int GadgetId;
|
||||||
|
private int ItemId;
|
||||||
|
private int Cd; // Probably hours
|
||||||
|
private boolean IsForbidGuest;
|
||||||
|
private boolean InitDisableInteract;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return this.PointType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGatherId() {
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGadgetId() {
|
||||||
|
return GadgetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getItemId() {
|
||||||
|
return ItemId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCd() {
|
||||||
|
return Cd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isForbidGuest() {
|
||||||
|
return IsForbidGuest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean initDisableInteract() {
|
||||||
|
return InitDisableInteract;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoad() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package emu.grasscutter.data.def;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.GameResource;
|
||||||
|
import emu.grasscutter.data.ResourceType;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@ResourceType(name = "InvestigationMonsterConfigData.json")
|
||||||
|
@Data
|
||||||
|
public class InvestigationMonsterData extends GameResource {
|
||||||
|
private int Id;
|
||||||
|
private int CityId;
|
||||||
|
private List<Integer> MonsterIdList;
|
||||||
|
private List<Integer> GroupIdList;
|
||||||
|
private int RewardPreviewId;
|
||||||
|
private String MapMarkCreateType;
|
||||||
|
private String MonsterCategory;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return this.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoad() {
|
||||||
|
super.onLoad();
|
||||||
|
}
|
||||||
|
}
|
@ -1,223 +0,0 @@
|
|||||||
package emu.grasscutter.game.dungeons;
|
|
||||||
|
|
||||||
import emu.grasscutter.data.common.ItemParamData;
|
|
||||||
import emu.grasscutter.data.excels.DungeonData;
|
|
||||||
import emu.grasscutter.game.entity.EntityMonster;
|
|
||||||
import emu.grasscutter.game.inventory.GameItem;
|
|
||||||
import emu.grasscutter.game.inventory.ItemType;
|
|
||||||
import emu.grasscutter.game.player.Player;
|
|
||||||
import emu.grasscutter.game.props.ActionReason;
|
|
||||||
import emu.grasscutter.game.world.Scene;
|
|
||||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
|
||||||
import emu.grasscutter.scripts.constants.EventType;
|
|
||||||
import emu.grasscutter.scripts.data.SceneGroup;
|
|
||||||
import emu.grasscutter.scripts.data.ScriptArgs;
|
|
||||||
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.PacketGadgetAutoPickDropInfoNotify;
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DungeonChallenge {
|
|
||||||
private final Scene scene;
|
|
||||||
private final SceneGroup group;
|
|
||||||
|
|
||||||
private int challengeIndex;
|
|
||||||
private int challengeId;
|
|
||||||
private boolean success;
|
|
||||||
private boolean progress;
|
|
||||||
/**
|
|
||||||
* has more challenge
|
|
||||||
*/
|
|
||||||
private boolean stage;
|
|
||||||
private int score;
|
|
||||||
private int objective = 0;
|
|
||||||
private IntSet rewardedPlayers;
|
|
||||||
|
|
||||||
public DungeonChallenge(Scene scene, SceneGroup group, int challengeId, int challengeIndex, int objective) {
|
|
||||||
this.scene = scene;
|
|
||||||
this.group = group;
|
|
||||||
this.challengeId = challengeId;
|
|
||||||
this.challengeIndex = challengeIndex;
|
|
||||||
this.objective = objective;
|
|
||||||
this.setRewardedPlayers(new IntOpenHashSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
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 int getObjective() {
|
|
||||||
return objective;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setObjective(int objective) {
|
|
||||||
this.objective = objective;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSuccess() {
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSuccess(boolean isSuccess) {
|
|
||||||
this.success = isSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean inProgress() {
|
|
||||||
return progress;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getScore() {
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isStage() {
|
|
||||||
return stage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStage(boolean stage) {
|
|
||||||
this.stage = stage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getTimeLimit() {
|
|
||||||
return 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntSet getRewardedPlayers() {
|
|
||||||
return rewardedPlayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRewardedPlayers(IntSet rewardedPlayers) {
|
|
||||||
this.rewardedPlayers = rewardedPlayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
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()) {
|
|
||||||
// Call success script event
|
|
||||||
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_SUCCESS, null);
|
|
||||||
|
|
||||||
// Settle
|
|
||||||
settle();
|
|
||||||
} else {
|
|
||||||
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_FAIL, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void settle() {
|
|
||||||
getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene()));
|
|
||||||
|
|
||||||
if(!stage){
|
|
||||||
getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE, new ScriptArgs(this.isSuccess() ? 1 : 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onMonsterDie(EntityMonster entity) {
|
|
||||||
score = getScore() + 1;
|
|
||||||
|
|
||||||
getScene().broadcastPacket(new PacketChallengeDataNotify(this, 1, getScore()));
|
|
||||||
|
|
||||||
if (getScore() >= getObjective() && this.progress) {
|
|
||||||
this.setSuccess(true);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<GameItem> rollRewards() {
|
|
||||||
List<GameItem> rewards = new ArrayList<>();
|
|
||||||
|
|
||||||
for (ItemParamData param : getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
|
|
||||||
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return rewards;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void getStatueDrops(Player player, GadgetInteractReq request) {
|
|
||||||
DungeonData dungeonData = getScene().getDungeonData();
|
|
||||||
int resinCost = dungeonData.getStatueCostCount() != 0 ? dungeonData.getStatueCostCount() : 20;
|
|
||||||
|
|
||||||
if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Already rewarded
|
|
||||||
if (getRewardedPlayers().contains(player.getUid())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get rewards.
|
|
||||||
List<GameItem> rewards = new ArrayList<>();
|
|
||||||
|
|
||||||
if (request.getIsUseCondenseResin()) {
|
|
||||||
// Check if condensed resin is usable here.
|
|
||||||
// For this, we use the following logic for now:
|
|
||||||
// The normal resin cost of the dungeon has to be 20.
|
|
||||||
if (resinCost != 20) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the player has condensed resin.
|
|
||||||
GameItem condensedResin = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(220007);
|
|
||||||
if (condensedResin == null || condensedResin.getCount() <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deduct.
|
|
||||||
player.getInventory().removeItem(condensedResin, 1);
|
|
||||||
|
|
||||||
// Roll rewards, twice (because condensed).
|
|
||||||
rewards.addAll(this.rollRewards());
|
|
||||||
rewards.addAll(this.rollRewards());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// If the player used regular resin, try to deduct.
|
|
||||||
// Stop if insufficient resin.
|
|
||||||
boolean success = player.getResinManager().useResin(resinCost);
|
|
||||||
if (!success) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Roll rewards.
|
|
||||||
rewards.addAll(this.rollRewards());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add rewards to player and send notification.
|
|
||||||
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
|
|
||||||
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
|
||||||
|
|
||||||
getRewardedPlayers().add(player.getUid());
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,91 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.common.ItemParamData;
|
||||||
|
import emu.grasscutter.data.def.DungeonData;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
|
||||||
|
import emu.grasscutter.game.inventory.GameItem;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.scripts.constants.EventType;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
|
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DungeonChallenge extends WorldChallenge {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* has more challenge
|
||||||
|
*/
|
||||||
|
private boolean stage;
|
||||||
|
private IntSet rewardedPlayers;
|
||||||
|
|
||||||
|
public DungeonChallenge(Scene scene, SceneGroup group,
|
||||||
|
int challengeId, int challengeIndex,
|
||||||
|
List<Integer> paramList,
|
||||||
|
int timeLimit, int goal,
|
||||||
|
List<ChallengeTrigger> challengeTriggers) {
|
||||||
|
super(scene, group, challengeId, challengeIndex, paramList, timeLimit, goal, challengeTriggers);
|
||||||
|
this.setRewardedPlayers(new IntOpenHashSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStage() {
|
||||||
|
return stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStage(boolean stage) {
|
||||||
|
this.stage = stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntSet getRewardedPlayers() {
|
||||||
|
return rewardedPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRewardedPlayers(IntSet rewardedPlayers) {
|
||||||
|
this.rewardedPlayers = rewardedPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void done() {
|
||||||
|
super.done();
|
||||||
|
if (this.isSuccess()) {
|
||||||
|
// Settle
|
||||||
|
settle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void settle() {
|
||||||
|
if(!stage){
|
||||||
|
getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene()));
|
||||||
|
getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE,
|
||||||
|
new ScriptArgs(this.isSuccess() ? 1 : 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getStatueDrops(Player player) {
|
||||||
|
DungeonData dungeonData = getScene().getDungeonData();
|
||||||
|
if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Already rewarded
|
||||||
|
if (getRewardedPlayers().contains(player.getUid())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<GameItem> rewards = new ArrayList<>();
|
||||||
|
for (ItemParamData param : getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
|
||||||
|
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
|
||||||
|
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
||||||
|
|
||||||
|
getRewardedPlayers().add(player.getUid());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,132 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
|
||||||
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
|
import emu.grasscutter.game.entity.EntityMonster;
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.scripts.constants.EventType;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
|
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketDungeonChallengeBeginNotify;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class WorldChallenge {
|
||||||
|
private final Scene scene;
|
||||||
|
private final SceneGroup group;
|
||||||
|
private final int challengeId;
|
||||||
|
private final int challengeIndex;
|
||||||
|
private final List<Integer> paramList;
|
||||||
|
private final int timeLimit;
|
||||||
|
private final List<ChallengeTrigger> challengeTriggers;
|
||||||
|
private boolean progress;
|
||||||
|
private boolean success;
|
||||||
|
private long startedAt;
|
||||||
|
private int finishedTime;
|
||||||
|
private final int goal;
|
||||||
|
private final AtomicInteger score;
|
||||||
|
|
||||||
|
public WorldChallenge(Scene scene, SceneGroup group,
|
||||||
|
int challengeId, int challengeIndex, List<Integer> paramList,
|
||||||
|
int timeLimit, int goal,
|
||||||
|
List<ChallengeTrigger> challengeTriggers){
|
||||||
|
this.scene = scene;
|
||||||
|
this.group = group;
|
||||||
|
this.challengeId = challengeId;
|
||||||
|
this.challengeIndex = challengeIndex;
|
||||||
|
this.paramList = paramList;
|
||||||
|
this.timeLimit = timeLimit;
|
||||||
|
this.challengeTriggers = challengeTriggers;
|
||||||
|
this.goal = goal;
|
||||||
|
this.score = new AtomicInteger(0);
|
||||||
|
}
|
||||||
|
public boolean inProgress(){
|
||||||
|
return this.progress;
|
||||||
|
}
|
||||||
|
public void onCheckTimeOut(){
|
||||||
|
if(!inProgress()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(timeLimit <= 0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
challengeTriggers.forEach(t -> t.onCheckTimeout(this));
|
||||||
|
}
|
||||||
|
public void start(){
|
||||||
|
if(inProgress()){
|
||||||
|
Grasscutter.getLogger().info("Could not start a in progress challenge.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.progress = true;
|
||||||
|
this.startedAt = System.currentTimeMillis();
|
||||||
|
getScene().broadcastPacket(new PacketDungeonChallengeBeginNotify(this));
|
||||||
|
challengeTriggers.forEach(t -> t.onBegin(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void done(){
|
||||||
|
if(!inProgress()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
finish(true);
|
||||||
|
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_SUCCESS,
|
||||||
|
// TODO record the time in PARAM2 and used in action
|
||||||
|
new ScriptArgs().setParam2(finishedTime));
|
||||||
|
|
||||||
|
challengeTriggers.forEach(t -> t.onFinish(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fail(){
|
||||||
|
if(!inProgress()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
finish(false);
|
||||||
|
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_FAIL, null);
|
||||||
|
challengeTriggers.forEach(t -> t.onFinish(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void finish(boolean success){
|
||||||
|
this.progress = false;
|
||||||
|
this.success = success;
|
||||||
|
this.finishedTime = (int)((System.currentTimeMillis() - this.startedAt) / 1000L);
|
||||||
|
getScene().broadcastPacket(new PacketDungeonChallengeFinishNotify(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int increaseScore(){
|
||||||
|
return score.incrementAndGet();
|
||||||
|
}
|
||||||
|
public void onMonsterDeath(EntityMonster monster){
|
||||||
|
if(!inProgress()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(monster.getGroupId() != getGroup().id){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.challengeTriggers.forEach(t -> t.onMonsterDeath(this, monster));
|
||||||
|
}
|
||||||
|
public void onGadgetDeath(EntityGadget gadget){
|
||||||
|
if(!inProgress()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(gadget.getGroupId() != getGroup().id){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.challengeTriggers.forEach(t -> t.onGadgetDeath(this, gadget));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onGadgetDamage(EntityGadget gadget){
|
||||||
|
if(!inProgress()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(gadget.getGroupId() != getGroup().id){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.challengeTriggers.forEach(t -> t.onGadgetDamage(this, gadget));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ChallengeFactory {
|
||||||
|
|
||||||
|
private static final List<ChallengeFactoryHandler> challengeFactoryHandlers = new ArrayList<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
challengeFactoryHandlers.add(new DungeonChallengeFactoryHandler());
|
||||||
|
challengeFactoryHandlers.add(new DungeonGuardChallengeFactoryHandler());
|
||||||
|
challengeFactoryHandlers.add(new KillGadgetChallengeFactoryHandler());
|
||||||
|
challengeFactoryHandlers.add(new KillMonsterChallengeFactoryHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WorldChallenge getChallenge(int param1, int param2, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group){
|
||||||
|
for(var handler : challengeFactoryHandlers){
|
||||||
|
if(!handler.isThisType(param1, param2, param3, param4, param5, param6, scene, group)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return handler.build(param1, param2, param3, param4, param5, param6, scene, group);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
|
|
||||||
|
public interface ChallengeFactoryHandler {
|
||||||
|
boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group);
|
||||||
|
WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group);
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger;
|
||||||
|
import emu.grasscutter.game.props.SceneType;
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DungeonChallengeFactoryHandler implements ChallengeFactoryHandler{
|
||||||
|
@Override
|
||||||
|
public boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
|
||||||
|
// ActiveChallenge with 1,1000,300,233101003,15,0
|
||||||
|
return scene.getSceneType() == SceneType.SCENE_DUNGEON
|
||||||
|
&& param4 == group.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
|
||||||
|
var realGroup = scene.getScriptManager().getGroupById(param4);
|
||||||
|
return new DungeonChallenge(
|
||||||
|
scene, realGroup,
|
||||||
|
challengeId, // Id
|
||||||
|
challengeIndex, // Index
|
||||||
|
List.of(param5, param3),
|
||||||
|
param3, // Limit
|
||||||
|
param5, // Goal
|
||||||
|
List.of(new InTimeTrigger(), new KillMonsterTrigger()));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.GuardTrigger;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger;
|
||||||
|
import emu.grasscutter.game.props.SceneType;
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DungeonGuardChallengeFactoryHandler implements ChallengeFactoryHandler{
|
||||||
|
@Override
|
||||||
|
public boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
|
||||||
|
// ActiveChallenge with 1,188,234101003,12,3030,0
|
||||||
|
return scene.getSceneType() == SceneType.SCENE_DUNGEON
|
||||||
|
&& param3 == group.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
|
||||||
|
var realGroup = scene.getScriptManager().getGroupById(param3);
|
||||||
|
return new DungeonChallenge(
|
||||||
|
scene, realGroup,
|
||||||
|
challengeId, // Id
|
||||||
|
challengeIndex, // Index
|
||||||
|
List.of(param4, 0),
|
||||||
|
0, // Limit
|
||||||
|
param5, // Goal
|
||||||
|
List.of(new GuardTrigger()));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.factory.ChallengeFactoryHandler;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.KillGadgetTrigger;
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class KillGadgetChallengeFactoryHandler implements ChallengeFactoryHandler {
|
||||||
|
@Override
|
||||||
|
public boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
|
||||||
|
// kill gadgets(explosive barrel) in time
|
||||||
|
// ActiveChallenge with 56,201,20,2,201,4
|
||||||
|
// open chest in time
|
||||||
|
// ActiveChallenge with 666,202,30,7,202,1
|
||||||
|
return challengeId == 201 || challengeId == 202;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
|
||||||
|
return new WorldChallenge(
|
||||||
|
scene, group,
|
||||||
|
challengeId, // Id
|
||||||
|
challengeIndex, // Index
|
||||||
|
List.of(param3, param6, 0),
|
||||||
|
param3, // Limit
|
||||||
|
param6, // Goal
|
||||||
|
List.of(new InTimeTrigger(), new KillGadgetTrigger())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger;
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class KillMonsterChallengeFactoryHandler implements ChallengeFactoryHandler{
|
||||||
|
@Override
|
||||||
|
public boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
|
||||||
|
// ActiveChallenge with 180,180,45,133108061,1,0
|
||||||
|
return challengeId == 180;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
|
||||||
|
var realGroup = scene.getScriptManager().getGroupById(param4);
|
||||||
|
return new WorldChallenge(
|
||||||
|
scene, realGroup,
|
||||||
|
challengeId, // Id
|
||||||
|
challengeIndex, // Index
|
||||||
|
List.of(param5, param3),
|
||||||
|
param3, // Limit
|
||||||
|
param5, // Goal
|
||||||
|
List.of(new KillMonsterTrigger(), new InTimeTrigger())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.trigger;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
|
import emu.grasscutter.game.entity.EntityMonster;
|
||||||
|
|
||||||
|
public abstract class ChallengeTrigger {
|
||||||
|
public void onBegin(WorldChallenge challenge){}
|
||||||
|
public void onFinish(WorldChallenge challenge){}
|
||||||
|
public void onMonsterDeath(WorldChallenge challenge, EntityMonster monster){}
|
||||||
|
public void onGadgetDeath(WorldChallenge challenge, EntityGadget gadget){}
|
||||||
|
public void onCheckTimeout(WorldChallenge challenge){}
|
||||||
|
public void onGadgetDamage(WorldChallenge challenge, EntityGadget gadget){}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.trigger;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
|
import emu.grasscutter.game.entity.EntityMonster;
|
||||||
|
import emu.grasscutter.game.props.FightProperty;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
|
||||||
|
|
||||||
|
public class GuardTrigger extends KillMonsterTrigger{
|
||||||
|
@Override
|
||||||
|
public void onBegin(WorldChallenge challenge) {
|
||||||
|
super.onBegin(challenge);
|
||||||
|
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGadgetDamage(WorldChallenge challenge, EntityGadget gadget) {
|
||||||
|
var curHp = gadget.getFightProperties().get(FightProperty.FIGHT_PROP_CUR_HP.getId());
|
||||||
|
var maxHp = gadget.getFightProperties().get(FightProperty.FIGHT_PROP_BASE_HP.getId());
|
||||||
|
int percent = (int) (curHp / maxHp);
|
||||||
|
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, percent));
|
||||||
|
|
||||||
|
if(percent <= 0){
|
||||||
|
challenge.fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.trigger;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
|
||||||
|
public class InTimeTrigger extends ChallengeTrigger{
|
||||||
|
@Override
|
||||||
|
public void onCheckTimeout(WorldChallenge challenge) {
|
||||||
|
var current = System.currentTimeMillis();
|
||||||
|
if(current - challenge.getStartedAt() > challenge.getTimeLimit() * 1000L){
|
||||||
|
challenge.fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.trigger;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
|
||||||
|
|
||||||
|
public class KillGadgetTrigger extends ChallengeTrigger{
|
||||||
|
@Override
|
||||||
|
public void onBegin(WorldChallenge challenge) {
|
||||||
|
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, challenge.getScore().get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGadgetDeath(WorldChallenge challenge, EntityGadget gadget) {
|
||||||
|
var newScore = challenge.increaseScore();
|
||||||
|
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, newScore));
|
||||||
|
|
||||||
|
if(newScore >= challenge.getGoal()){
|
||||||
|
challenge.done();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.trigger;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.game.entity.EntityMonster;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
|
||||||
|
|
||||||
|
public class KillMonsterTrigger extends ChallengeTrigger{
|
||||||
|
@Override
|
||||||
|
public void onBegin(WorldChallenge challenge) {
|
||||||
|
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 1, challenge.getScore().get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMonsterDeath(WorldChallenge challenge, EntityMonster monster) {
|
||||||
|
var newScore = challenge.increaseScore();
|
||||||
|
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 1, newScore));
|
||||||
|
|
||||||
|
if(newScore >= challenge.getGoal()){
|
||||||
|
challenge.done();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,13 @@
|
|||||||
package emu.grasscutter.game.entity;
|
package emu.grasscutter.game.entity;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.excels.GadgetData;
|
import emu.grasscutter.data.excels.GadgetData;
|
||||||
|
import emu.grasscutter.game.entity.gadget.*;
|
||||||
import emu.grasscutter.game.props.EntityIdType;
|
import emu.grasscutter.game.props.EntityIdType;
|
||||||
import emu.grasscutter.game.props.EntityType;
|
import emu.grasscutter.game.props.EntityType;
|
||||||
|
import emu.grasscutter.game.props.LifeState;
|
||||||
import emu.grasscutter.game.props.PlayerProperty;
|
import emu.grasscutter.game.props.PlayerProperty;
|
||||||
import emu.grasscutter.game.world.Scene;
|
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.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
|
||||||
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
|
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
|
||||||
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
|
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
|
||||||
@ -23,15 +20,17 @@ 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.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
|
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
|
||||||
import emu.grasscutter.net.proto.WorktopInfoOuterClass.WorktopInfo;
|
import emu.grasscutter.scripts.constants.EventType;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGadget;
|
||||||
|
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketGadgetStateNotify;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
|
||||||
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.Int2FloatOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import lombok.ToString;
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
|
||||||
|
|
||||||
|
@ToString(callSuper = true)
|
||||||
public class EntityGadget extends EntityBaseGadget {
|
public class EntityGadget extends EntityBaseGadget {
|
||||||
private final GadgetData data;
|
private final GadgetData data;
|
||||||
private final Position pos;
|
private final Position pos;
|
||||||
@ -39,7 +38,9 @@ public class EntityGadget extends EntityBaseGadget {
|
|||||||
private int gadgetId;
|
private int gadgetId;
|
||||||
|
|
||||||
private int state;
|
private int state;
|
||||||
private IntSet worktopOptions;
|
private int pointType;
|
||||||
|
private GadgetContent content;
|
||||||
|
private SceneGadget metaGadget;
|
||||||
|
|
||||||
public EntityGadget(Scene scene, int gadgetId, Position pos) {
|
public EntityGadget(Scene scene, int gadgetId, Position pos) {
|
||||||
super(scene);
|
super(scene);
|
||||||
@ -50,19 +51,22 @@ public class EntityGadget extends EntityBaseGadget {
|
|||||||
this.rot = new Position();
|
this.rot = new Position();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EntityGadget(Scene scene, int gadgetId, Position pos, GadgetContent content) {
|
||||||
|
this(scene, gadgetId, pos);
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
public GadgetData getGadgetData() {
|
public GadgetData getGadgetData() {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Position getPosition() {
|
public Position getPosition() {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return this.pos;
|
return this.pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Position getRotation() {
|
public Position getRotation() {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return this.rot;
|
return this.rot;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,33 +86,72 @@ public class EntityGadget extends EntityBaseGadget {
|
|||||||
this.state = state;
|
this.state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IntSet getWorktopOptions() {
|
public void updateState(int state){
|
||||||
return worktopOptions;
|
this.setState(state);
|
||||||
|
this.getScene().broadcastPacket(new PacketGadgetStateNotify(this, state));
|
||||||
|
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_STATE_CHANGE, new ScriptArgs(state, this.getConfigId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addWorktopOptions(int[] options) {
|
public int getPointType() {
|
||||||
if (this.worktopOptions == null) {
|
return pointType;
|
||||||
this.worktopOptions = new IntOpenHashSet();
|
|
||||||
}
|
|
||||||
Arrays.stream(options).forEach(this.worktopOptions::add);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeWorktopOption(int option) {
|
public void setPointType(int pointType) {
|
||||||
if (this.worktopOptions == null) {
|
this.pointType = pointType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GadgetContent getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated // Dont use!
|
||||||
|
public void setContent(GadgetContent content) {
|
||||||
|
this.content = this.content == null ? content : this.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SceneGadget getMetaGadget() {
|
||||||
|
return metaGadget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMetaGadget(SceneGadget metaGadget) {
|
||||||
|
this.metaGadget = metaGadget;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO refactor
|
||||||
|
public void buildContent() {
|
||||||
|
if (getContent() != null || getGadgetData() == null || getGadgetData().getType() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.worktopOptions.remove(option);
|
|
||||||
|
EntityType type = getGadgetData().getType();
|
||||||
|
GadgetContent content = switch (type) {
|
||||||
|
case GatherPoint -> new GadgetGatherPoint(this);
|
||||||
|
case Worktop -> new GadgetWorktop(this);
|
||||||
|
case RewardStatue -> new GadgetRewardStatue(this);
|
||||||
|
case Chest -> new GadgetChest(this);
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Int2FloatOpenHashMap getFightProperties() {
|
public Int2FloatOpenHashMap getFightProperties() {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDeath(int killerId) {
|
public void onCreate() {
|
||||||
|
// Lua event
|
||||||
|
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(this.getConfigId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeath(int killerId) {
|
||||||
|
if(getScene().getChallenge() != null){
|
||||||
|
getScene().getChallenge().onGadgetDeath(this);
|
||||||
|
}
|
||||||
|
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_GADGET_DIE, new ScriptArgs(this.getConfigId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -143,15 +186,16 @@ public class EntityGadget extends EntityBaseGadget {
|
|||||||
.setIsEnableInteract(true)
|
.setIsEnableInteract(true)
|
||||||
.setAuthorityPeerId(this.getScene().getWorld().getHostPeerId());
|
.setAuthorityPeerId(this.getScene().getWorld().getHostPeerId());
|
||||||
|
|
||||||
if (this.getGadgetData().getType() == EntityType.Worktop && this.getWorktopOptions() != null) {
|
if (this.getContent() != null) {
|
||||||
WorktopInfo worktop = WorktopInfo.newBuilder()
|
this.getContent().onBuildProto(gadgetInfo);
|
||||||
.addAllOptionList(this.getWorktopOptions())
|
|
||||||
.build();
|
|
||||||
gadgetInfo.setWorktop(worktop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entityInfo.setGadget(gadgetInfo);
|
entityInfo.setGadget(gadgetInfo);
|
||||||
|
|
||||||
return entityInfo.build();
|
return entityInfo.build();
|
||||||
}
|
}
|
||||||
|
public void die() {
|
||||||
|
getScene().broadcastPacket(new PacketLifeStateChangeNotify(this, LifeState.LIFE_DEAD));
|
||||||
|
this.onDeath(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,11 @@ import emu.grasscutter.data.GameData;
|
|||||||
import emu.grasscutter.data.common.PropGrowCurve;
|
import emu.grasscutter.data.common.PropGrowCurve;
|
||||||
import emu.grasscutter.data.excels.MonsterCurveData;
|
import emu.grasscutter.data.excels.MonsterCurveData;
|
||||||
import emu.grasscutter.data.excels.MonsterData;
|
import emu.grasscutter.data.excels.MonsterData;
|
||||||
import emu.grasscutter.game.dungeons.DungeonChallenge;
|
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
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;
|
||||||
import emu.grasscutter.game.world.Scene;
|
import emu.grasscutter.game.world.Scene;
|
||||||
import emu.grasscutter.game.world.World;
|
|
||||||
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
|
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
|
||||||
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
|
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
|
||||||
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
|
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
|
||||||
@ -25,6 +23,7 @@ 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.scripts.constants.EventType;
|
||||||
|
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||||
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;
|
||||||
@ -112,6 +111,12 @@ public class EntityMonster extends GameEntity {
|
|||||||
this.poseId = poseId;
|
this.poseId = poseId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
// Lua event
|
||||||
|
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(this.getConfigId()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void damage(float amount, int killerId) {
|
public void damage(float amount, int killerId) {
|
||||||
// Get HP before damage.
|
// Get HP before damage.
|
||||||
@ -135,8 +140,8 @@ public class EntityMonster extends GameEntity {
|
|||||||
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
|
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
|
||||||
}
|
}
|
||||||
// first set the challenge data
|
// first set the challenge data
|
||||||
if (getScene().getChallenge() != null && getScene().getChallenge().getGroup().id == this.getGroupId()) {
|
if (getScene().getChallenge() != null) {
|
||||||
getScene().getChallenge().onMonsterDie(this);
|
getScene().getChallenge().onMonsterDeath(this);
|
||||||
}
|
}
|
||||||
if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) {
|
if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) {
|
||||||
if(getScene().getScriptManager().getScriptMonsterSpawnService() != null){
|
if(getScene().getScriptManager().getScriptMonsterSpawnService() != null){
|
||||||
@ -144,7 +149,9 @@ public class EntityMonster extends GameEntity {
|
|||||||
}
|
}
|
||||||
// prevent spawn monster after success
|
// prevent spawn monster after success
|
||||||
if(getScene().getChallenge() != null && getScene().getChallenge().inProgress()){
|
if(getScene().getChallenge() != null && getScene().getChallenge().inProgress()){
|
||||||
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, null);
|
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
|
||||||
|
}else if(getScene().getChallenge() == null){
|
||||||
|
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
81
src/main/java/emu/grasscutter/game/entity/EntityNPC.java
Normal file
81
src/main/java/emu/grasscutter/game/entity/EntityNPC.java
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package emu.grasscutter.game.entity;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.props.EntityIdType;
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.net.proto.*;
|
||||||
|
import emu.grasscutter.scripts.data.SceneNPC;
|
||||||
|
import emu.grasscutter.utils.Position;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
|
||||||
|
|
||||||
|
public class EntityNPC extends GameEntity{
|
||||||
|
|
||||||
|
private final Position position;
|
||||||
|
private final Position rotation;
|
||||||
|
private final SceneNPC metaNpc;
|
||||||
|
private final int suiteId;
|
||||||
|
|
||||||
|
public EntityNPC(Scene scene, SceneNPC metaNPC, int blockId, int suiteId) {
|
||||||
|
super(scene);
|
||||||
|
this.id = getScene().getWorld().getNextEntityId(EntityIdType.NPC);
|
||||||
|
setConfigId(metaNPC.config_id);
|
||||||
|
setGroupId(metaNPC.group.id);
|
||||||
|
setBlockId(blockId);
|
||||||
|
this.suiteId = suiteId;
|
||||||
|
this.position = metaNPC.pos.clone();
|
||||||
|
this.rotation = metaNPC.rot.clone();
|
||||||
|
this.metaNpc = metaNPC;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Int2FloatOpenHashMap getFightProperties() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Position getPosition() {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Position getRotation() {
|
||||||
|
return rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSuiteId() {
|
||||||
|
return suiteId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
|
||||||
|
|
||||||
|
EntityAuthorityInfoOuterClass.EntityAuthorityInfo authority = EntityAuthorityInfoOuterClass.EntityAuthorityInfo.newBuilder()
|
||||||
|
.setAbilityInfo(AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo.newBuilder())
|
||||||
|
.setRendererChangedInfo(EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo.newBuilder())
|
||||||
|
.setAiInfo(SceneEntityAiInfoOuterClass.SceneEntityAiInfo.newBuilder()
|
||||||
|
.setIsAiOpen(true)
|
||||||
|
.setBornPos(getPosition().toProto()))
|
||||||
|
.setBornPos(getPosition().toProto())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
SceneEntityInfoOuterClass.SceneEntityInfo.Builder entityInfo = SceneEntityInfoOuterClass.SceneEntityInfo.newBuilder()
|
||||||
|
.setEntityId(getId())
|
||||||
|
.setEntityType(ProtEntityTypeOuterClass.ProtEntityType.PROT_ENTITY_NPC)
|
||||||
|
.setMotionInfo(MotionInfoOuterClass.MotionInfo.newBuilder()
|
||||||
|
.setPos(getPosition().toProto())
|
||||||
|
.setRot(getRotation().toProto())
|
||||||
|
.setSpeed(VectorOuterClass.Vector.newBuilder()))
|
||||||
|
.addAnimatorParaList(AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair.newBuilder())
|
||||||
|
.setEntityClientData(EntityClientDataOuterClass.EntityClientData.newBuilder())
|
||||||
|
.setEntityAuthorityInfo(authority)
|
||||||
|
.setLifeState(1);
|
||||||
|
|
||||||
|
|
||||||
|
entityInfo.setNpc(SceneNpcInfoOuterClass.SceneNpcInfo.newBuilder()
|
||||||
|
.setNpcId(metaNpc.npc_id)
|
||||||
|
.setBlockId(getBlockId())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
return entityInfo.build();
|
||||||
|
}
|
||||||
|
}
|
@ -108,10 +108,6 @@ public abstract class GameEntity {
|
|||||||
this.lastMoveReliableSeq = lastMoveReliableSeq;
|
this.lastMoveReliableSeq = lastMoveReliableSeq;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract SceneEntityInfo toProto();
|
|
||||||
|
|
||||||
public abstract void onDeath(int killerId);
|
|
||||||
|
|
||||||
public void setFightProperty(FightProperty prop, float value) {
|
public void setFightProperty(FightProperty prop, float value) {
|
||||||
this.getFightProperties().put(prop.getId(), value);
|
this.getFightProperties().put(prop.getId(), value);
|
||||||
}
|
}
|
||||||
@ -219,4 +215,21 @@ public abstract class GameEntity {
|
|||||||
getScene().killEntity(this, killerId);
|
getScene().killEntity(this, killerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when this entity is added to the world
|
||||||
|
*/
|
||||||
|
public void onCreate() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when this entity dies
|
||||||
|
* @param killerId Entity id of the entity that killed this entity
|
||||||
|
*/
|
||||||
|
public void onDeath(int killerId) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract SceneEntityInfo toProto();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
package emu.grasscutter.game.entity.gadget;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.net.proto.BossChestInfoOuterClass.BossChestInfo;
|
||||||
|
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.InteractTypeOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
||||||
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
|
import emu.grasscutter.scripts.constants.ScriptGadgetState;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
|
||||||
|
|
||||||
|
import static emu.grasscutter.net.proto.InterOpTypeOuterClass.InterOpType.INTER_OP_START;
|
||||||
|
|
||||||
|
public class GadgetChest extends GadgetContent {
|
||||||
|
|
||||||
|
public GadgetChest(EntityGadget gadget) {
|
||||||
|
super(gadget);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
|
||||||
|
var chestInteractHandlerMap = getGadget().getScene().getWorld().getServer().getWorldDataManager().getChestInteractHandlerMap();
|
||||||
|
var handler = chestInteractHandlerMap.get(getGadget().getGadgetData().getJsonName());
|
||||||
|
if(handler == null){
|
||||||
|
Grasscutter.getLogger().warn("Could not found the handler of this type of Chests {}", getGadget().getGadgetData().getJsonName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(opType == INTER_OP_START && handler.isTwoStep()){
|
||||||
|
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_OPEN_CHEST, INTER_OP_START));
|
||||||
|
return false;
|
||||||
|
}else{
|
||||||
|
var success = handler.onInteract(this, player);
|
||||||
|
if (!success){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getGadget().updateState(ScriptGadgetState.ChestOpened);
|
||||||
|
player.sendPacket(new PacketGadgetInteractRsp(this.getGadget(), InteractTypeOuterClass.InteractType.INTERACT_OPEN_CHEST));
|
||||||
|
// let the chest disappear
|
||||||
|
getGadget().die();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
||||||
|
if(getGadget().getMetaGadget() == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bossChest = getGadget().getMetaGadget().boss_chest;
|
||||||
|
if(bossChest != null){
|
||||||
|
var players = getGadget().getScene().getPlayers().stream().map(Player::getUid).toList();
|
||||||
|
|
||||||
|
gadgetInfo.setBossChest(BossChestInfo.newBuilder()
|
||||||
|
.setMonsterConfigId(bossChest.monster_config_id)
|
||||||
|
.setResin(bossChest.resin)
|
||||||
|
.addAllQualifyUidList(players)
|
||||||
|
.addAllRemainUidList(players)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package emu.grasscutter.game.entity.gadget;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
|
|
||||||
|
public abstract class GadgetContent {
|
||||||
|
private final EntityGadget gadget;
|
||||||
|
|
||||||
|
public GadgetContent(EntityGadget gadget) {
|
||||||
|
this.gadget = gadget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityGadget getGadget() {
|
||||||
|
return gadget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType);
|
||||||
|
|
||||||
|
public abstract void onBuildProto(SceneGadgetInfo.Builder gadgetInfo);
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package emu.grasscutter.game.entity.gadget;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.GameData;
|
||||||
|
import emu.grasscutter.data.def.GatherData;
|
||||||
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
|
import emu.grasscutter.game.inventory.GameItem;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
|
import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo;
|
||||||
|
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
|
|
||||||
|
public class GadgetGatherPoint extends GadgetContent {
|
||||||
|
private GatherData gatherData;
|
||||||
|
|
||||||
|
public GadgetGatherPoint(EntityGadget gadget) {
|
||||||
|
super(gadget);
|
||||||
|
this.gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public GatherData getGatherData() {
|
||||||
|
return gatherData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getItemId() {
|
||||||
|
return getGatherData().getItemId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
|
||||||
|
GameItem item = new GameItem(gatherData.getItemId(), 1);
|
||||||
|
|
||||||
|
player.getInventory().addItem(item, ActionReason.Gather);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
||||||
|
GatherGadgetInfo gatherGadgetInfo = GatherGadgetInfo.newBuilder()
|
||||||
|
.setItemId(this.getItemId())
|
||||||
|
.setIsForbidGuest(this.getGatherData().isForbidGuest())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
gadgetInfo.setGatherGadget(gatherGadgetInfo);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package emu.grasscutter.game.entity.gadget;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
|
||||||
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
||||||
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
|
||||||
|
|
||||||
|
public class GadgetRewardStatue extends GadgetContent {
|
||||||
|
|
||||||
|
public GadgetRewardStatue(EntityGadget gadget) {
|
||||||
|
super(gadget);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
|
||||||
|
if (player.getScene().getChallenge() != null && player.getScene().getChallenge() instanceof DungeonChallenge dungeonChallenge) {
|
||||||
|
dungeonChallenge.getStatueDrops(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_OPEN_STATUE));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package emu.grasscutter.game.entity.gadget;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
|
import emu.grasscutter.net.proto.WorktopInfoOuterClass.WorktopInfo;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||||
|
|
||||||
|
public class GadgetWorktop extends GadgetContent {
|
||||||
|
private IntSet worktopOptions;
|
||||||
|
|
||||||
|
public GadgetWorktop(EntityGadget gadget) {
|
||||||
|
super(gadget);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
||||||
|
if (this.worktopOptions == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WorktopInfo worktop = WorktopInfo.newBuilder()
|
||||||
|
.addAllOptionList(this.getWorktopOptions())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
gadgetInfo.setWorktop(worktop);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package emu.grasscutter.game.entity.gadget.chest;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.data.common.ItemParamData;
|
||||||
|
import emu.grasscutter.game.entity.gadget.GadgetChest;
|
||||||
|
import emu.grasscutter.game.inventory.GameItem;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BossChestInteractHandler implements ChestInteractHandler{
|
||||||
|
@Override
|
||||||
|
public boolean isTwoStep() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onInteract(GadgetChest chest, Player player) {
|
||||||
|
var worldDataManager = chest.getGadget().getScene().getWorld().getServer().getWorldDataManager();
|
||||||
|
var monster = chest.getGadget().getMetaGadget().group.monsters.get(chest.getGadget().getMetaGadget().boss_chest.monster_config_id);
|
||||||
|
var reward = worldDataManager.getRewardByBossId(monster.monster_id);
|
||||||
|
|
||||||
|
if(reward == null){
|
||||||
|
Grasscutter.getLogger().warn("Could not found the reward of boss monster {}", monster.monster_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<GameItem> rewards = new ArrayList<>();
|
||||||
|
for (ItemParamData param : reward.getPreviewItems()) {
|
||||||
|
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
player.getInventory().addItems(rewards, ActionReason.OpenWorldBossChest);
|
||||||
|
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package emu.grasscutter.game.entity.gadget.chest;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.entity.gadget.GadgetChest;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
|
||||||
|
public interface ChestInteractHandler {
|
||||||
|
|
||||||
|
boolean isTwoStep();
|
||||||
|
|
||||||
|
boolean onInteract(GadgetChest chest, Player player);
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package emu.grasscutter.game.entity.gadget.chest;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.entity.gadget.GadgetChest;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.game.world.ChestReward;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class NormalChestInteractHandler implements ChestInteractHandler {
|
||||||
|
private final ChestReward chestReward;
|
||||||
|
|
||||||
|
public NormalChestInteractHandler(ChestReward rewardData){
|
||||||
|
this.chestReward = rewardData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTwoStep() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onInteract(GadgetChest chest, Player player) {
|
||||||
|
player.earnExp(chestReward.getAdvExp());
|
||||||
|
player.getInventory().addItem(201, chestReward.getResin());
|
||||||
|
|
||||||
|
var mora = chestReward.getMora() * (1 + (player.getWorldLevel() - 1) * 0.5);
|
||||||
|
player.getInventory().addItem(202, (int)mora);
|
||||||
|
|
||||||
|
for(int i=0;i<chestReward.getContent().size();i++){
|
||||||
|
chest.getGadget().getScene().addItemEntity(chestReward.getContent().get(i).getItemId(), chestReward.getContent().get(i).getCount(), chest.getGadget());
|
||||||
|
}
|
||||||
|
|
||||||
|
var random = new Random(System.currentTimeMillis());
|
||||||
|
for(int i=0;i<chestReward.getRandomCount();i++){
|
||||||
|
var index = random.nextInt(chestReward.getRandomContent().size());
|
||||||
|
var item = chestReward.getRandomContent().get(index);
|
||||||
|
chest.getGadget().getScene().addItemEntity(item.getItemId(), item.getCount(), chest.getGadget());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -37,7 +37,6 @@ import emu.grasscutter.game.managers.mapmark.*;
|
|||||||
import emu.grasscutter.game.managers.stamina.StaminaManager;
|
import emu.grasscutter.game.managers.stamina.StaminaManager;
|
||||||
import emu.grasscutter.game.managers.SotSManager;
|
import emu.grasscutter.game.managers.SotSManager;
|
||||||
import emu.grasscutter.game.props.ActionReason;
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
import emu.grasscutter.game.props.EntityType;
|
|
||||||
import emu.grasscutter.game.props.PlayerProperty;
|
import emu.grasscutter.game.props.PlayerProperty;
|
||||||
import emu.grasscutter.game.props.SceneType;
|
import emu.grasscutter.game.props.SceneType;
|
||||||
import emu.grasscutter.game.quest.QuestManager;
|
import emu.grasscutter.game.quest.QuestManager;
|
||||||
@ -988,7 +987,8 @@ public class Player {
|
|||||||
return this.getMailHandler().replaceMailByIndex(index, message);
|
return this.getMailHandler().replaceMailByIndex(index, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void interactWith(int gadgetEntityId, GadgetInteractReq request) {
|
|
||||||
|
public void interactWith(int gadgetEntityId, InterOpTypeOuterClass.InterOpType opType) {
|
||||||
GameEntity entity = getScene().getEntityById(gadgetEntityId);
|
GameEntity entity = getScene().getEntityById(gadgetEntityId);
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
return;
|
return;
|
||||||
@ -1016,11 +1016,15 @@ public class Player {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (entity instanceof EntityGadget gadget) {
|
} else if (entity instanceof EntityGadget gadget) {
|
||||||
if (gadget.getGadgetData().getType() == EntityType.RewardStatue) {
|
|
||||||
if (scene.getChallenge() != null) {
|
if (gadget.getContent() == null) {
|
||||||
scene.getChallenge().getStatueDrops(this, request);
|
return;
|
||||||
}
|
}
|
||||||
this.sendPacket(new PacketGadgetInteractRsp(gadget, InteractType.INTERACT_TYPE_OPEN_STATUE));
|
|
||||||
|
boolean shouldDelete = gadget.getContent().onInteract(this, opType);
|
||||||
|
|
||||||
|
if (shouldDelete) {
|
||||||
|
entity.getScene().removeEntity(entity);
|
||||||
}
|
}
|
||||||
} else if (entity instanceof EntityMonster monster) {
|
} else if (entity instanceof EntityMonster monster) {
|
||||||
insectCaptureManager.arrestSmallCreature(monster);
|
insectCaptureManager.arrestSmallCreature(monster);
|
||||||
|
22
src/main/java/emu/grasscutter/game/world/ChestReward.java
Normal file
22
src/main/java/emu/grasscutter/game/world/ChestReward.java
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package emu.grasscutter.game.world;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.inventory.ItemDef;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.FieldDefaults;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
|
public class ChestReward {
|
||||||
|
List<String> objNames;
|
||||||
|
int advExp;
|
||||||
|
int resin;
|
||||||
|
int mora;
|
||||||
|
int sigil;
|
||||||
|
List<ItemDef> content;
|
||||||
|
int randomCount;
|
||||||
|
List<ItemDef> randomContent;
|
||||||
|
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
package emu.grasscutter.game.world;
|
package emu.grasscutter.game.world;
|
||||||
|
|
||||||
|
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.excels.*;
|
import emu.grasscutter.data.excels.*;
|
||||||
import emu.grasscutter.game.dungeons.DungeonChallenge;
|
|
||||||
import emu.grasscutter.game.dungeons.DungeonSettleListener;
|
import emu.grasscutter.game.dungeons.DungeonSettleListener;
|
||||||
import emu.grasscutter.game.entity.*;
|
import emu.grasscutter.game.entity.*;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
@ -13,24 +13,24 @@ import emu.grasscutter.game.props.FightProperty;
|
|||||||
import emu.grasscutter.game.props.LifeState;
|
import emu.grasscutter.game.props.LifeState;
|
||||||
import emu.grasscutter.game.props.SceneType;
|
import emu.grasscutter.game.props.SceneType;
|
||||||
import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry;
|
import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
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.SceneIndexManager;
|
||||||
import emu.grasscutter.scripts.SceneScriptManager;
|
import emu.grasscutter.scripts.SceneScriptManager;
|
||||||
import emu.grasscutter.scripts.data.SceneBlock;
|
import emu.grasscutter.scripts.data.SceneBlock;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGadget;
|
||||||
import emu.grasscutter.scripts.data.SceneGroup;
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
import emu.grasscutter.server.packet.send.PacketAvatarSkillInfoNotify;
|
import emu.grasscutter.server.packet.send.*;
|
||||||
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
|
import emu.grasscutter.utils.Position;
|
||||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
|
||||||
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
|
|
||||||
import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
|
|
||||||
import emu.grasscutter.server.packet.send.PacketSceneEntityDisappearNotify;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import org.danilopianini.util.SpatialIndex;
|
import org.danilopianini.util.SpatialIndex;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class Scene {
|
public class Scene {
|
||||||
private final World world;
|
private final World world;
|
||||||
@ -49,12 +49,11 @@ public class Scene {
|
|||||||
private int weather;
|
private int weather;
|
||||||
|
|
||||||
private SceneScriptManager scriptManager;
|
private SceneScriptManager scriptManager;
|
||||||
private DungeonChallenge challenge;
|
private WorldChallenge challenge;
|
||||||
private List<DungeonSettleListener> dungeonSettleListeners;
|
private List<DungeonSettleListener> dungeonSettleListeners;
|
||||||
private DungeonData dungeonData;
|
private DungeonData dungeonData;
|
||||||
private int prevScene; // Id of the previous scene
|
private int prevScene; // Id of the previous scene
|
||||||
private int prevScenePoint;
|
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;
|
||||||
@ -198,11 +197,11 @@ public class Scene {
|
|||||||
this.dungeonData = dungeonData;
|
this.dungeonData = dungeonData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DungeonChallenge getChallenge() {
|
public WorldChallenge getChallenge() {
|
||||||
return challenge;
|
return challenge;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setChallenge(DungeonChallenge challenge) {
|
public void setChallenge(WorldChallenge challenge) {
|
||||||
this.challenge = challenge;
|
this.challenge = challenge;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,6 +309,7 @@ public class Scene {
|
|||||||
|
|
||||||
private void addEntityDirectly(GameEntity entity) {
|
private void addEntityDirectly(GameEntity entity) {
|
||||||
getEntities().put(entity.getId(), entity);
|
getEntities().put(entity.getId(), entity);
|
||||||
|
entity.onCreate(); // Call entity create event
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void addEntity(GameEntity entity) {
|
public synchronized void addEntity(GameEntity entity) {
|
||||||
@ -320,14 +320,21 @@ public class Scene {
|
|||||||
public synchronized void addEntityToSingleClient(Player player, GameEntity entity) {
|
public synchronized void addEntityToSingleClient(Player player, GameEntity entity) {
|
||||||
this.addEntityDirectly(entity);
|
this.addEntityDirectly(entity);
|
||||||
player.sendPacket(new PacketSceneEntityAppearNotify(entity));
|
player.sendPacket(new PacketSceneEntityAppearNotify(entity));
|
||||||
|
|
||||||
|
}
|
||||||
|
public void addEntities(Collection<? extends GameEntity> entities){
|
||||||
|
addEntities(entities, VisionType.VISION_TYPE_BORN);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void addEntities(Collection<GameEntity> entities) {
|
public synchronized void addEntities(Collection<? extends GameEntity> entities, VisionType visionType) {
|
||||||
|
if(entities == null || entities.isEmpty()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (GameEntity entity : entities) {
|
for (GameEntity entity : entities) {
|
||||||
this.addEntityDirectly(entity);
|
this.addEntityDirectly(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.broadcastPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VISION_TYPE_BORN));
|
this.broadcastPacket(new PacketSceneEntityAppearNotify(entities, visionType));
|
||||||
}
|
}
|
||||||
|
|
||||||
private GameEntity removeEntityDirectly(GameEntity entity) {
|
private GameEntity removeEntityDirectly(GameEntity entity) {
|
||||||
@ -344,7 +351,14 @@ public class Scene {
|
|||||||
this.broadcastPacket(new PacketSceneEntityDisappearNotify(removed, visionType));
|
this.broadcastPacket(new PacketSceneEntityDisappearNotify(removed, visionType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public synchronized void removeEntities(List<GameEntity> entity, VisionType visionType) {
|
||||||
|
var toRemove = entity.stream()
|
||||||
|
.map(this::removeEntityDirectly)
|
||||||
|
.toList();
|
||||||
|
if (toRemove.size() > 0) {
|
||||||
|
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, visionType));
|
||||||
|
}
|
||||||
|
}
|
||||||
public synchronized void replaceEntity(EntityAvatar oldEntity, EntityAvatar newEntity) {
|
public synchronized void replaceEntity(EntityAvatar oldEntity, EntityAvatar newEntity) {
|
||||||
this.removeEntityDirectly(oldEntity);
|
this.removeEntityDirectly(oldEntity);
|
||||||
this.addEntityDirectly(newEntity);
|
this.addEntityDirectly(newEntity);
|
||||||
@ -424,9 +438,12 @@ public class Scene {
|
|||||||
// TEMPORARY
|
// TEMPORARY
|
||||||
this.checkSpawns();
|
this.checkSpawns();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Triggers
|
// Triggers
|
||||||
this.getScriptManager().onTick();
|
this.scriptManager.checkRegions();
|
||||||
|
|
||||||
|
if(challenge != null){
|
||||||
|
challenge.onCheckTimeOut();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - Test
|
// TODO - Test
|
||||||
@ -503,17 +520,16 @@ public class Scene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<SceneBlock> getPlayerActiveBlocks(Player player){
|
||||||
|
// consider the borders' entities of blocks, so we check if contains by index
|
||||||
|
return SceneIndexManager.queryNeighbors(getScriptManager().getBlocksIndex(),
|
||||||
|
player.getPos().toXZDoubleArray(), Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange);
|
||||||
|
}
|
||||||
public void checkBlocks() {
|
public void checkBlocks() {
|
||||||
Set<SceneBlock> visible = new HashSet<>();
|
Set<SceneBlock> visible = new HashSet<>();
|
||||||
|
|
||||||
for (Player player : this.getPlayers()) {
|
for (Player player : this.getPlayers()) {
|
||||||
for (SceneBlock block : getScriptManager().getBlocks()) {
|
var blocks = getPlayerActiveBlocks(player);
|
||||||
if (!block.contains(player.getPos())) {
|
visible.addAll(blocks);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
visible.add(block);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator<SceneBlock> it = this.getLoadedBlocks().iterator();
|
Iterator<SceneBlock> it = this.getLoadedBlocks().iterator();
|
||||||
@ -527,58 +543,128 @@ public class Scene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (SceneBlock block : visible) {
|
for(var block : visible){
|
||||||
if (!this.getLoadedBlocks().contains(block)) {
|
if (!this.getLoadedBlocks().contains(block)) {
|
||||||
this.onLoadBlock(block);
|
this.onLoadBlock(block, this.getPlayers());
|
||||||
this.getLoadedBlocks().add(block);
|
this.getLoadedBlocks().add(block);
|
||||||
|
}else{
|
||||||
|
// dynamic load the groups for players in a loaded block
|
||||||
|
var toLoad = this.getPlayers().stream()
|
||||||
|
.filter(p -> block.contains(p.getPos()))
|
||||||
|
.map(p -> playerMeetGroups(p, block))
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.toList();
|
||||||
|
onLoadGroup(toLoad);
|
||||||
|
}
|
||||||
|
for (Player player : this.getPlayers()) {
|
||||||
|
getScriptManager().meetEntities(loadNpcForPlayer(player, block));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public List<SceneGroup> playerMeetGroups(Player player, SceneBlock block){
|
||||||
|
List<SceneGroup> sceneGroups = SceneIndexManager.queryNeighbors(block.sceneGroupIndex, player.getPos().toDoubleArray(),
|
||||||
|
Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange);
|
||||||
|
|
||||||
|
List<SceneGroup> groups = sceneGroups.stream()
|
||||||
|
.filter(group -> !scriptManager.getLoadedGroupSetPerBlock().get(block.id).contains(group))
|
||||||
|
.peek(group -> scriptManager.getLoadedGroupSetPerBlock().get(block.id).add(group))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (groups.size() == 0) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
public void onLoadBlock(SceneBlock block, List<Player> players) {
|
||||||
|
this.getScriptManager().loadBlockFromScript(block);
|
||||||
|
scriptManager.getLoadedGroupSetPerBlock().put(block.id , new HashSet<>());
|
||||||
|
|
||||||
|
// the groups form here is not added in current scene
|
||||||
|
var groups = players.stream()
|
||||||
|
.filter(player -> block.contains(player.getPos()))
|
||||||
|
.map(p -> playerMeetGroups(p, block))
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
onLoadGroup(groups);
|
||||||
|
Grasscutter.getLogger().info("Scene {} Block {} loaded.", this.getId(), block.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO optimize
|
public void onLoadGroup(List<SceneGroup> groups){
|
||||||
public void onLoadBlock(SceneBlock block) {
|
if(groups == null || groups.isEmpty()){
|
||||||
for (SceneGroup group : block.groups) {
|
return;
|
||||||
|
}
|
||||||
|
for (SceneGroup group : groups) {
|
||||||
// We load the script files for the groups here
|
// We load the script files for the groups here
|
||||||
if (!group.isLoaded()) {
|
this.getScriptManager().loadGroupFromScript(group);
|
||||||
this.getScriptManager().loadGroupFromScript(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
group.triggers.forEach(getScriptManager()::registerTrigger);
|
|
||||||
group.regions.forEach(getScriptManager()::registerRegion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spawn gadgets AFTER triggers are added
|
// Spawn gadgets AFTER triggers are added
|
||||||
// TODO
|
// TODO
|
||||||
for (SceneGroup group : block.groups) {
|
var entities = new ArrayList<GameEntity>();
|
||||||
|
for (SceneGroup group : groups) {
|
||||||
if (group.init_config == null) {
|
if (group.init_config == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load garbages
|
||||||
|
List<SceneGadget> garbageGadgets = group.getGarbageGadgets();
|
||||||
|
|
||||||
|
if (garbageGadgets != null) {
|
||||||
|
entities.addAll(garbageGadgets.stream().map(g -> scriptManager.createGadget(group.id, group.block_id, g))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load suites
|
||||||
int suite = group.init_config.suite;
|
int suite = group.init_config.suite;
|
||||||
|
|
||||||
if (suite == 0) {
|
if (suite == 0 || group.suites == null || group.suites.size() == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
// just load the 'init' suite, avoid spawn the suite added by AddExtraGroupSuite etc.
|
||||||
this.getScriptManager().spawnGadgetsInGroup(group, suite);
|
var suiteData = group.getSuiteByIndex(suite);
|
||||||
suite++;
|
suiteData.sceneTriggers.forEach(getScriptManager()::registerTrigger);
|
||||||
} while (suite < group.init_config.end_suite);
|
|
||||||
|
entities.addAll(suiteData.sceneGadgets.stream()
|
||||||
|
.map(g -> scriptManager.createGadget(group.id, group.block_id, g))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.toList());
|
||||||
|
entities.addAll(suiteData.sceneMonsters.stream()
|
||||||
|
.map(mob -> scriptManager.createMonster(group.id, group.block_id, mob))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.toList());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scriptManager.meetEntities(entities);
|
||||||
|
//scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null);
|
||||||
|
//groups.forEach(g -> scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null));
|
||||||
|
Grasscutter.getLogger().info("Scene {} loaded {} group(s)", this.getId(), groups.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onUnloadBlock(SceneBlock block) {
|
public void onUnloadBlock(SceneBlock block) {
|
||||||
List<GameEntity> toRemove = this.getEntities().values().stream().filter(e -> e.getBlockId() == block.id).toList();
|
List<GameEntity> toRemove = this.getEntities().values().stream()
|
||||||
|
.filter(e -> e.getBlockId() == block.id).toList();
|
||||||
|
|
||||||
if (toRemove.size() > 0) {
|
if (toRemove.size() > 0) {
|
||||||
toRemove.stream().forEach(this::removeEntityDirectly);
|
toRemove.forEach(this::removeEntityDirectly);
|
||||||
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE));
|
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (SceneGroup group : block.groups) {
|
for (SceneGroup group : block.groups.values()) {
|
||||||
group.triggers.forEach(getScriptManager()::deregisterTrigger);
|
if(group.triggers != null){
|
||||||
group.regions.forEach(getScriptManager()::deregisterRegion);
|
group.triggers.values().forEach(getScriptManager()::deregisterTrigger);
|
||||||
|
}
|
||||||
|
if(group.regions != null){
|
||||||
|
group.regions.forEach(getScriptManager()::deregisterRegion);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
scriptManager.getLoadedGroupSetPerBlock().remove(block.id);
|
||||||
|
Grasscutter.getLogger().info("Scene {} Block {} is unloaded.", this.getId(), block.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gadgets
|
// Gadgets
|
||||||
@ -643,4 +729,65 @@ public class Scene {
|
|||||||
player.getSession().send(packet);
|
player.getSession().send(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addItemEntity(int itemId, int amount, GameEntity bornForm){
|
||||||
|
ItemData itemData = GameData.getItemDataMap().get(itemId);
|
||||||
|
if (itemData == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (itemData.isEquip()) {
|
||||||
|
float range = (3f + (.1f * amount));
|
||||||
|
for (int i = 0; i < amount; i++) {
|
||||||
|
Position pos = bornForm.getPosition().clone().addX((float) (Math.random() * range) - (range / 2)).addZ((float) (Math.random() * range) - (range / 2)).addZ(.9f);
|
||||||
|
EntityItem entity = new EntityItem(this, null, itemData, pos, 1);
|
||||||
|
addEntity(entity);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
EntityItem entity = new EntityItem(this, null, itemData, bornForm.getPosition().clone().addZ(.9f), amount);
|
||||||
|
addEntity(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public List<EntityNPC> loadNpcForPlayer(Player player, SceneBlock block){
|
||||||
|
if(!block.contains(player.getPos())){
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
var pos = player.getPos();
|
||||||
|
var data = GameData.getSceneNpcBornData().get(getId());
|
||||||
|
if(data == null){
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
var npcs = SceneIndexManager.queryNeighbors(data.getIndex(), pos.toDoubleArray(),
|
||||||
|
Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange);
|
||||||
|
var entityNPCS = npcs.stream().map(item -> {
|
||||||
|
var group = data.getGroups().get(item.getGroupId());
|
||||||
|
if(group == null){
|
||||||
|
group = SceneGroup.of(item.getGroupId());
|
||||||
|
data.getGroups().putIfAbsent(item.getGroupId(), group);
|
||||||
|
group.load(getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(group.npc == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var npc = group.npc.get(item.getConfigId());
|
||||||
|
if(npc == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getScriptManager().createNPC(npc, block.id, item.getSuiteIdList().get(0));
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.filter(item -> getEntities().values().stream()
|
||||||
|
.filter(e -> e instanceof EntityNPC)
|
||||||
|
.noneMatch(e -> e.getConfigId() == item.getConfigId()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if(entityNPCS.size() > 0){
|
||||||
|
broadcastPacket(new PacketGroupSuiteNotify(entityNPCS));
|
||||||
|
}
|
||||||
|
|
||||||
|
return entityNPCS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
package emu.grasscutter.game.world;
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.data.DataLoader;
|
||||||
|
import emu.grasscutter.data.GameData;
|
||||||
|
import emu.grasscutter.data.def.RewardPreviewData;
|
||||||
|
import emu.grasscutter.game.entity.gadget.chest.BossChestInteractHandler;
|
||||||
|
import emu.grasscutter.game.entity.gadget.chest.ChestInteractHandler;
|
||||||
|
import emu.grasscutter.game.entity.gadget.chest.NormalChestInteractHandler;
|
||||||
|
import emu.grasscutter.server.game.GameServer;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class WorldDataManager {
|
||||||
|
private final GameServer gameServer;
|
||||||
|
private final Map<String, ChestInteractHandler> chestInteractHandlerMap; // chestType-Handler
|
||||||
|
|
||||||
|
public WorldDataManager(GameServer gameServer){
|
||||||
|
this.gameServer = gameServer;
|
||||||
|
this.chestInteractHandlerMap = new HashMap<>();
|
||||||
|
loadChestConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void loadChestConfig(){
|
||||||
|
// set the special chest first
|
||||||
|
chestInteractHandlerMap.put("SceneObj_Chest_Flora", new BossChestInteractHandler());
|
||||||
|
|
||||||
|
try(InputStream is = DataLoader.load("ChestReward.json"); InputStreamReader isr = new InputStreamReader(is)) {
|
||||||
|
List<ChestReward> chestReward = Grasscutter.getGsonFactory().fromJson(
|
||||||
|
isr,
|
||||||
|
TypeToken.getParameterized(List.class, ChestReward.class).getType());
|
||||||
|
|
||||||
|
chestReward.forEach(reward ->
|
||||||
|
reward.getObjNames().forEach(
|
||||||
|
name -> chestInteractHandlerMap.putIfAbsent(name, new NormalChestInteractHandler(reward))));
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Grasscutter.getLogger().error("Unable to load chest reward config.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, ChestInteractHandler> getChestInteractHandlerMap() {
|
||||||
|
return chestInteractHandlerMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RewardPreviewData getRewardByBossId(int monsterId){
|
||||||
|
var investigationMonsterData = GameData.getInvestigationMonsterDataMap().values().parallelStream()
|
||||||
|
.filter(imd -> imd.getMonsterIdList() != null && !imd.getMonsterIdList().isEmpty())
|
||||||
|
.filter(imd -> imd.getMonsterIdList().get(0) == monsterId)
|
||||||
|
.findFirst();
|
||||||
|
|
||||||
|
if(investigationMonsterData.isEmpty()){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return GameData.getRewardPreviewDataMap().get(investigationMonsterData.get().getRewardPreviewId());
|
||||||
|
}
|
||||||
|
}
|
33
src/main/java/emu/grasscutter/scripts/SceneIndexManager.java
Normal file
33
src/main/java/emu/grasscutter/scripts/SceneIndexManager.java
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package emu.grasscutter.scripts;
|
||||||
|
|
||||||
|
import com.github.davidmoten.rtreemulti.Entry;
|
||||||
|
import com.github.davidmoten.rtreemulti.RTree;
|
||||||
|
import com.github.davidmoten.rtreemulti.geometry.Geometry;
|
||||||
|
import com.github.davidmoten.rtreemulti.geometry.Rectangle;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class SceneIndexManager {
|
||||||
|
|
||||||
|
public static <T> RTree<T, Geometry> buildIndex(int dimensions, Collection<T> elements, Function<T, Geometry> extractor){
|
||||||
|
RTree<T, Geometry> rtree = RTree.dimensions(dimensions).create();
|
||||||
|
return rtree.add(elements.stream().map(e -> Entry.entry(e, extractor.apply(e))).toList());
|
||||||
|
}
|
||||||
|
public static <T> List<T> queryNeighbors(RTree<T, Geometry> tree, double[] position, int range){
|
||||||
|
var result = new ArrayList<T>();
|
||||||
|
Rectangle rectangle = Rectangle.create(calRange(position, -range), calRange(position, range));
|
||||||
|
var queryResult = tree.search(rectangle);
|
||||||
|
queryResult.forEach(q -> result.add(q.value()));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
private static double[] calRange(double[] position, int range){
|
||||||
|
var newPos = position.clone();
|
||||||
|
for(int i=0;i<newPos.length;i++){
|
||||||
|
newPos[i] += range;
|
||||||
|
}
|
||||||
|
return newPos;
|
||||||
|
}
|
||||||
|
}
|
@ -1,75 +1,70 @@
|
|||||||
package emu.grasscutter.scripts;
|
package emu.grasscutter.scripts;
|
||||||
|
|
||||||
import java.util.*;
|
import com.github.davidmoten.rtreemulti.RTree;
|
||||||
import java.util.stream.Collectors;
|
import com.github.davidmoten.rtreemulti.geometry.Geometry;
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
import javax.script.Bindings;
|
import emu.grasscutter.data.GameData;
|
||||||
import javax.script.CompiledScript;
|
import emu.grasscutter.data.def.MonsterData;
|
||||||
import javax.script.ScriptException;
|
import emu.grasscutter.data.def.WorldLevelData;
|
||||||
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
|
import emu.grasscutter.game.entity.EntityMonster;
|
||||||
|
import emu.grasscutter.game.entity.EntityNPC;
|
||||||
|
import emu.grasscutter.game.entity.GameEntity;
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.net.proto.VisionTypeOuterClass;
|
||||||
|
import emu.grasscutter.scripts.constants.EventType;
|
||||||
|
import emu.grasscutter.scripts.data.*;
|
||||||
import emu.grasscutter.scripts.service.ScriptMonsterSpawnService;
|
import emu.grasscutter.scripts.service.ScriptMonsterSpawnService;
|
||||||
import emu.grasscutter.scripts.service.ScriptMonsterTideService;
|
import emu.grasscutter.scripts.service.ScriptMonsterTideService;
|
||||||
|
import io.netty.util.concurrent.FastThreadLocalThread;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import org.luaj.vm2.LuaError;
|
import org.luaj.vm2.LuaError;
|
||||||
import org.luaj.vm2.LuaValue;
|
import org.luaj.vm2.LuaValue;
|
||||||
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
|
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import java.util.*;
|
||||||
import emu.grasscutter.game.entity.EntityGadget;
|
import java.util.concurrent.ExecutorService;
|
||||||
import emu.grasscutter.game.world.Scene;
|
import java.util.concurrent.LinkedBlockingDeque;
|
||||||
import emu.grasscutter.scripts.constants.EventType;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import emu.grasscutter.scripts.data.SceneBlock;
|
import java.util.concurrent.TimeUnit;
|
||||||
import emu.grasscutter.scripts.data.SceneConfig;
|
import java.util.stream.Collectors;
|
||||||
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.SceneRegion;
|
|
||||||
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;
|
|
||||||
|
|
||||||
import static emu.grasscutter.Configuration.*;
|
|
||||||
|
|
||||||
public class SceneScriptManager {
|
public class SceneScriptManager {
|
||||||
private final Scene scene;
|
private final Scene scene;
|
||||||
private final ScriptLib scriptLib;
|
|
||||||
private final LuaValue scriptLibLua;
|
|
||||||
private final Map<String, Integer> variables;
|
private final Map<String, Integer> variables;
|
||||||
private Bindings bindings;
|
private SceneMeta meta;
|
||||||
private SceneConfig config;
|
|
||||||
private List<SceneBlock> blocks;
|
|
||||||
private boolean isInit;
|
private boolean isInit;
|
||||||
/**
|
|
||||||
* SceneTrigger Set
|
|
||||||
*/
|
|
||||||
private final Map<String, SceneTrigger> triggers;
|
|
||||||
/**
|
/**
|
||||||
* current triggers controlled by RefreshGroup
|
* current triggers controlled by RefreshGroup
|
||||||
*/
|
*/
|
||||||
private final Int2ObjectOpenHashMap<Set<SceneTrigger>> currentTriggers;
|
private final Int2ObjectOpenHashMap<Set<SceneTrigger>> currentTriggers;
|
||||||
private final Int2ObjectOpenHashMap<SceneRegion> regions;
|
private final Int2ObjectOpenHashMap<SceneRegion> regions;
|
||||||
private Map<Integer,SceneGroup> sceneGroups;
|
private Map<Integer,SceneGroup> sceneGroups;
|
||||||
private SceneGroup currentGroup;
|
|
||||||
private ScriptMonsterTideService scriptMonsterTideService;
|
private ScriptMonsterTideService scriptMonsterTideService;
|
||||||
private ScriptMonsterSpawnService scriptMonsterSpawnService;
|
private ScriptMonsterSpawnService scriptMonsterSpawnService;
|
||||||
|
/**
|
||||||
|
* blockid - loaded groupSet
|
||||||
|
*/
|
||||||
|
private Int2ObjectMap<Set<SceneGroup>> loadedGroupSetPerBlock;
|
||||||
|
public static final ExecutorService eventExecutor;
|
||||||
|
static {
|
||||||
|
eventExecutor = new ThreadPoolExecutor(4, 4,
|
||||||
|
60, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1000),
|
||||||
|
FastThreadLocalThread::new, new ThreadPoolExecutor.AbortPolicy());
|
||||||
|
}
|
||||||
public SceneScriptManager(Scene scene) {
|
public SceneScriptManager(Scene scene) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
this.scriptLib = new ScriptLib(this);
|
|
||||||
this.scriptLibLua = CoerceJavaToLua.coerce(this.scriptLib);
|
|
||||||
this.triggers = new HashMap<>();
|
|
||||||
this.currentTriggers = new Int2ObjectOpenHashMap<>();
|
this.currentTriggers = new Int2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
this.regions = new Int2ObjectOpenHashMap<>();
|
this.regions = new Int2ObjectOpenHashMap<>();
|
||||||
this.variables = new HashMap<>();
|
this.variables = new HashMap<>();
|
||||||
this.sceneGroups = new HashMap<>();
|
this.sceneGroups = new HashMap<>();
|
||||||
this.scriptMonsterSpawnService = new ScriptMonsterSpawnService(this);
|
this.scriptMonsterSpawnService = new ScriptMonsterSpawnService(this);
|
||||||
|
this.loadedGroupSetPerBlock = new Int2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
// TEMPORARY
|
// TEMPORARY
|
||||||
if (this.getScene().getId() < 10) {
|
if (this.getScene().getId() < 10 && !Grasscutter.getConfig().server.game.enableScriptInBigWorld) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,28 +76,15 @@ public class SceneScriptManager {
|
|||||||
return scene;
|
return scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ScriptLib getScriptLib() {
|
|
||||||
return scriptLib;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LuaValue getScriptLibLua() {
|
|
||||||
return scriptLibLua;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Bindings getBindings() {
|
|
||||||
return bindings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SceneConfig getConfig() {
|
public SceneConfig getConfig() {
|
||||||
return config;
|
if(!isInit){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return meta.config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SceneGroup getCurrentGroup() {
|
public Map<Integer, SceneBlock> getBlocks() {
|
||||||
return currentGroup;
|
return meta.blocks;
|
||||||
}
|
|
||||||
|
|
||||||
public List<SceneBlock> getBlocks() {
|
|
||||||
return blocks;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Integer> getVariables() {
|
public Map<String, Integer> getVariables() {
|
||||||
@ -112,29 +94,31 @@ public class SceneScriptManager {
|
|||||||
public Set<SceneTrigger> getTriggersByEvent(int eventId) {
|
public Set<SceneTrigger> getTriggersByEvent(int eventId) {
|
||||||
return currentTriggers.computeIfAbsent(eventId, e -> new HashSet<>());
|
return currentTriggers.computeIfAbsent(eventId, e -> new HashSet<>());
|
||||||
}
|
}
|
||||||
|
public void registerTrigger(List<SceneTrigger> triggers) {
|
||||||
|
triggers.forEach(this::registerTrigger);
|
||||||
|
}
|
||||||
public void registerTrigger(SceneTrigger trigger) {
|
public void registerTrigger(SceneTrigger trigger) {
|
||||||
this.triggers.put(trigger.name, trigger);
|
|
||||||
getTriggersByEvent(trigger.event).add(trigger);
|
getTriggersByEvent(trigger.event).add(trigger);
|
||||||
}
|
}
|
||||||
|
public void deregisterTrigger(List<SceneTrigger> triggers) {
|
||||||
|
triggers.forEach(this::deregisterTrigger);
|
||||||
|
}
|
||||||
public void deregisterTrigger(SceneTrigger trigger) {
|
public void deregisterTrigger(SceneTrigger trigger) {
|
||||||
this.triggers.remove(trigger.name);
|
|
||||||
getTriggersByEvent(trigger.event).remove(trigger);
|
getTriggersByEvent(trigger.event).remove(trigger);
|
||||||
}
|
}
|
||||||
public void resetTriggers(List<String> triggerNames) {
|
public void resetTriggers(int eventId) {
|
||||||
for(var name : triggerNames){
|
currentTriggers.put(eventId, new HashSet<>());
|
||||||
var instance = triggers.get(name);
|
|
||||||
this.currentTriggers.get(instance.event).clear();
|
|
||||||
this.currentTriggers.get(instance.event).add(instance);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public void refreshGroup(SceneGroup group, int suiteIndex){
|
public void refreshGroup(SceneGroup group, int suiteIndex){
|
||||||
var suite = group.getSuiteByIndex(suiteIndex);
|
var suite = group.getSuiteByIndex(suiteIndex);
|
||||||
if(suite == null){
|
if(suite == null){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(suite.triggers.size() > 0){
|
if(suite.sceneTriggers.size() > 0){
|
||||||
resetTriggers(suite.triggers);
|
for(var trigger : suite.sceneTriggers){
|
||||||
|
resetTriggers(trigger.event);
|
||||||
|
this.currentTriggers.get(trigger.event).add(trigger);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
spawnMonstersInGroup(group, suite);
|
spawnMonstersInGroup(group, suite);
|
||||||
spawnGadgetsInGroup(group, suite);
|
spawnGadgetsInGroup(group, suite);
|
||||||
@ -151,57 +135,32 @@ public class SceneScriptManager {
|
|||||||
regions.remove(region.config_id);
|
regions.remove(region.config_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Int2ObjectMap<Set<SceneGroup>> getLoadedGroupSetPerBlock() {
|
||||||
|
return loadedGroupSetPerBlock;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO optimize
|
// TODO optimize
|
||||||
public SceneGroup getGroupById(int groupId) {
|
public SceneGroup getGroupById(int groupId) {
|
||||||
for (SceneBlock block : this.getScene().getLoadedBlocks()) {
|
for (SceneBlock block : this.getScene().getLoadedBlocks()) {
|
||||||
for (SceneGroup group : block.groups) {
|
var group = block.groups.get(groupId);
|
||||||
if (group.id == groupId) {
|
if(group == null){
|
||||||
return group;
|
continue;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!group.isLoaded()){
|
||||||
|
getScene().onLoadGroup(List.of(group));
|
||||||
|
}
|
||||||
|
return group;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
// Get compiled script if cached
|
var meta = ScriptLoader.getSceneMeta(getScene().getId());
|
||||||
CompiledScript cs = ScriptLoader.getScriptByPath(
|
if (meta == null){
|
||||||
SCRIPT("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("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(i);
|
|
||||||
block.id = blockIds.get(i);
|
|
||||||
|
|
||||||
loadBlockFromScript(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.blocks = blocks;
|
|
||||||
} catch (ScriptException e) {
|
|
||||||
Grasscutter.getLogger().error("Error running script", e);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.meta = meta;
|
||||||
|
|
||||||
// TEMP
|
// TEMP
|
||||||
this.isInit = true;
|
this.isInit = true;
|
||||||
@ -211,91 +170,24 @@ public class SceneScriptManager {
|
|||||||
return isInit;
|
return isInit;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadBlockFromScript(SceneBlock block) {
|
public void loadBlockFromScript(SceneBlock block) {
|
||||||
CompiledScript cs = ScriptLoader.getScriptByPath(
|
block.load(scene.getId(), meta.context);
|
||||||
SCRIPT("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) {
|
public void loadGroupFromScript(SceneGroup group) {
|
||||||
// Set flag here so if there is no script, we dont call this function over and over again.
|
group.load(getScene().getId());
|
||||||
group.setLoaded(true);
|
|
||||||
|
|
||||||
CompiledScript cs = ScriptLoader.getScriptByPath(
|
if (group.variables != null) {
|
||||||
SCRIPT("Scene/" + getScene().getId() + "/scene" + getScene().getId() + "_group" + group.id + "." + ScriptLoader.getScriptType()));
|
group.variables.forEach(var -> this.getVariables().put(var.name, var.value));
|
||||||
|
|
||||||
if (cs == null) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eval script
|
this.sceneGroups.put(group.id, group);
|
||||||
try {
|
|
||||||
cs.eval(getBindings());
|
|
||||||
|
|
||||||
// Set
|
if(group.regions != null){
|
||||||
group.monsters = ScriptLoader.getSerializer().toList(SceneMonster.class, bindings.get("monsters")).stream()
|
group.regions.forEach(this::registerRegion);
|
||||||
.collect(Collectors.toMap(x -> x.config_id, y -> y));
|
|
||||||
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.regions = ScriptLoader.getSerializer().toList(SceneRegion.class, bindings.get("regions"));
|
|
||||||
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
|
|
||||||
Int2ObjectMap<Object> map = new Int2ObjectOpenHashMap<>();
|
|
||||||
group.monsters.entrySet().forEach(m -> map.put(m.getValue().config_id, m));
|
|
||||||
group.gadgets.forEach(m -> map.put(m.config_id, m));
|
|
||||||
|
|
||||||
for (SceneSuite suite : group.suites) {
|
|
||||||
suite.sceneMonsters = new ArrayList<>(suite.monsters.size());
|
|
||||||
suite.monsters.forEach(id -> {
|
|
||||||
Object objEntry = map.get(id.intValue());
|
|
||||||
if (objEntry instanceof Map.Entry<?,?> monsterEntry) {
|
|
||||||
Object monster = monsterEntry.getValue();
|
|
||||||
if(monster instanceof SceneMonster sceneMonster){
|
|
||||||
suite.sceneMonsters.add(sceneMonster);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
suite.sceneGadgets = new ArrayList<>(suite.gadgets.size());
|
|
||||||
for (int id : suite.gadgets) {
|
|
||||||
try {
|
|
||||||
SceneGadget gadget = (SceneGadget) map.get(id);
|
|
||||||
if (gadget != null) {
|
|
||||||
suite.sceneGadgets.add(gadget);
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.sceneGroups.put(group.id, group);
|
|
||||||
} catch (ScriptException e) {
|
|
||||||
Grasscutter.getLogger().error("Error loading group " + group.id + " in scene " + getScene().getId(), e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onTick() {
|
|
||||||
checkRegions();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void checkRegions() {
|
public void checkRegions() {
|
||||||
if (this.regions.size() == 0) {
|
if (this.regions.size() == 0) {
|
||||||
return;
|
return;
|
||||||
@ -316,6 +208,16 @@ public class SceneScriptManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addGroupSuite(SceneGroup group, SceneSuite suite){
|
||||||
|
spawnMonstersInGroup(group, suite);
|
||||||
|
spawnGadgetsInGroup(group, suite);
|
||||||
|
registerTrigger(suite.sceneTriggers);
|
||||||
|
}
|
||||||
|
public void removeGroupSuite(SceneGroup group, SceneSuite suite){
|
||||||
|
removeMonstersInGroup(group, suite);
|
||||||
|
removeGadgetsInGroup(group, suite);
|
||||||
|
deregisterTrigger(suite.sceneTriggers);
|
||||||
|
}
|
||||||
public void spawnGadgetsInGroup(SceneGroup group, int suiteIndex) {
|
public void spawnGadgetsInGroup(SceneGroup group, int suiteIndex) {
|
||||||
spawnGadgetsInGroup(group, group.getSuiteByIndex(suiteIndex));
|
spawnGadgetsInGroup(group, group.getSuiteByIndex(suiteIndex));
|
||||||
}
|
}
|
||||||
@ -325,26 +227,17 @@ public class SceneScriptManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void spawnGadgetsInGroup(SceneGroup group, SceneSuite suite) {
|
public void spawnGadgetsInGroup(SceneGroup group, SceneSuite suite) {
|
||||||
List<SceneGadget> gadgets = group.gadgets;
|
var gadgets = group.gadgets.values();
|
||||||
|
|
||||||
if (suite != null) {
|
if (suite != null) {
|
||||||
gadgets = suite.sceneGadgets;
|
gadgets = suite.sceneGadgets;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (SceneGadget g : gadgets) {
|
var toCreate = gadgets.stream()
|
||||||
EntityGadget entity = new EntityGadget(getScene(), g.gadget_id, g.pos);
|
.map(g -> createGadget(g.group.id, group.block_id, g))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
if (entity.getGadgetData() == null) continue;
|
.toList();
|
||||||
|
this.addEntities(toCreate);
|
||||||
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) {
|
public void spawnMonstersInGroup(SceneGroup group, int suiteIndex) {
|
||||||
@ -358,17 +251,16 @@ public class SceneScriptManager {
|
|||||||
if(suite == null || suite.sceneMonsters.size() <= 0){
|
if(suite == null || suite.sceneMonsters.size() <= 0){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.currentGroup = group;
|
this.addEntities(suite.sceneMonsters.stream()
|
||||||
suite.sceneMonsters.forEach(mob -> this.scriptMonsterSpawnService.spawnMonster(group.id, mob));
|
.map(mob -> createMonster(group.id, group.block_id, mob)).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void spawnMonstersInGroup(SceneGroup group) {
|
public void spawnMonstersInGroup(SceneGroup group) {
|
||||||
this.currentGroup = group;
|
this.addEntities(group.monsters.values().stream()
|
||||||
group.monsters.values().forEach(mob -> this.scriptMonsterSpawnService.spawnMonster(group.id, mob));
|
.map(mob -> createMonster(group.id, group.block_id, mob)).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startMonsterTideInGroup(SceneGroup group, Integer[] ordersConfigId, int tideCount, int sceneLimit) {
|
public void startMonsterTideInGroup(SceneGroup group, Integer[] ordersConfigId, int tideCount, int sceneLimit) {
|
||||||
this.currentGroup = group;
|
|
||||||
this.scriptMonsterTideService =
|
this.scriptMonsterTideService =
|
||||||
new ScriptMonsterTideService(this, group, tideCount, sceneLimit, ordersConfigId);
|
new ScriptMonsterTideService(this, group, tideCount, sceneLimit, ordersConfigId);
|
||||||
|
|
||||||
@ -379,49 +271,71 @@ public class SceneScriptManager {
|
|||||||
}
|
}
|
||||||
this.getScriptMonsterTideService().unload();
|
this.getScriptMonsterTideService().unload();
|
||||||
}
|
}
|
||||||
public void spawnMonstersByConfigId(int configId, int delayTime) {
|
public void spawnMonstersByConfigId(SceneGroup group, int configId, int delayTime) {
|
||||||
// TODO delay
|
// TODO delay
|
||||||
this.scriptMonsterSpawnService.spawnMonster(this.currentGroup.id, this.currentGroup.monsters.get(configId));
|
getScene().addEntity(createMonster(group.id, group.block_id, group.monsters.get(configId)));
|
||||||
}
|
}
|
||||||
// Events
|
// Events
|
||||||
|
public void callEvent(int eventType, ScriptArgs params){
|
||||||
|
/**
|
||||||
|
* We use ThreadLocal to trans SceneScriptManager context to ScriptLib, to avoid eval script for every groups' trigger in every scene instances.
|
||||||
|
* But when callEvent is called in a ScriptLib func, it may cause NPE because the inner call cleans the ThreadLocal so that outer call could not get it.
|
||||||
|
* e.g. CallEvent -> set -> ScriptLib.xxx -> CallEvent -> set -> remove -> NPE -> (remove)
|
||||||
|
* So we use thread pool to clean the stack to avoid this new issue.
|
||||||
|
*/
|
||||||
|
eventExecutor.submit(() -> this.realCallEvent(eventType, params));
|
||||||
|
}
|
||||||
|
|
||||||
public void callEvent(int eventType, ScriptArgs params) {
|
private void realCallEvent(int eventType, ScriptArgs params) {
|
||||||
for (SceneTrigger trigger : this.getTriggersByEvent(eventType)) {
|
try{
|
||||||
LuaValue condition = null;
|
ScriptLoader.getScriptLib().setSceneScriptManager(this);
|
||||||
|
for (SceneTrigger trigger : this.getTriggersByEvent(eventType)) {
|
||||||
|
try{
|
||||||
|
ScriptLoader.getScriptLib().setCurrentGroup(trigger.currentGroup);
|
||||||
|
|
||||||
if (trigger.condition != null && !trigger.condition.isEmpty()) {
|
LuaValue ret = callScriptFunc(trigger.condition, trigger.currentGroup, params);
|
||||||
condition = (LuaValue) this.getBindings().get(trigger.condition);
|
Grasscutter.getLogger().trace("Call Condition Trigger {}", trigger.condition);
|
||||||
}
|
|
||||||
|
|
||||||
LuaValue ret = LuaValue.TRUE;
|
if (ret.isboolean() && ret.checkboolean()) {
|
||||||
|
// the SetGroupVariableValueByGroup in tower need the param to record the first stage time
|
||||||
|
callScriptFunc(trigger.action, trigger.currentGroup, params);
|
||||||
|
Grasscutter.getLogger().trace("Call Action Trigger {}", trigger.action);
|
||||||
|
}
|
||||||
|
//TODO some ret may not bool
|
||||||
|
|
||||||
if (condition != null) {
|
}finally {
|
||||||
LuaValue args = LuaValue.NIL;
|
ScriptLoader.getScriptLib().removeCurrentGroup();
|
||||||
|
|
||||||
if (params != null) {
|
|
||||||
args = CoerceJavaToLua.coerce(params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptLib.logger.trace("Call Condition Trigger {}", trigger);
|
|
||||||
ret = safetyCall(trigger.condition, condition, args);
|
|
||||||
}
|
}
|
||||||
|
}finally {
|
||||||
if (ret.isboolean() && ret.checkboolean()) {
|
// make sure it is removed
|
||||||
ScriptLib.logger.trace("Call Action Trigger {}", trigger);
|
ScriptLoader.getScriptLib().removeSceneScriptManager();
|
||||||
LuaValue action = (LuaValue) this.getBindings().get(trigger.action);
|
|
||||||
// TODO impl the param of SetGroupVariableValueByGroup
|
|
||||||
var arg = new ScriptArgs();
|
|
||||||
arg.param2 = 100;
|
|
||||||
var args = CoerceJavaToLua.coerce(arg);
|
|
||||||
safetyCall(trigger.action, action, args);
|
|
||||||
}
|
|
||||||
//TODO some ret may not bool
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LuaValue callScriptFunc(String funcName, SceneGroup group, ScriptArgs params){
|
||||||
|
LuaValue funcLua = null;
|
||||||
|
if (funcName != null && !funcName.isEmpty()) {
|
||||||
|
funcLua = (LuaValue) group.getBindings().get(funcName);
|
||||||
|
}
|
||||||
|
|
||||||
|
LuaValue ret = LuaValue.TRUE;
|
||||||
|
|
||||||
|
if (funcLua != null) {
|
||||||
|
LuaValue args = LuaValue.NIL;
|
||||||
|
|
||||||
|
if (params != null) {
|
||||||
|
args = CoerceJavaToLua.coerce(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = safetyCall(funcName, funcLua, args);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
public LuaValue safetyCall(String name, LuaValue func, LuaValue args){
|
public LuaValue safetyCall(String name, LuaValue func, LuaValue args){
|
||||||
try{
|
try{
|
||||||
return func.call(this.getScriptLibLua(), args);
|
return func.call(ScriptLoader.getScriptLibLua(), args);
|
||||||
}catch (LuaError error){
|
}catch (LuaError error){
|
||||||
ScriptLib.logger.error("[LUA] call trigger failed {},{}",name,args,error);
|
ScriptLib.logger.error("[LUA] call trigger failed {},{}",name,args,error);
|
||||||
return LuaValue.valueOf(-1);
|
return LuaValue.valueOf(-1);
|
||||||
@ -436,4 +350,102 @@ public class SceneScriptManager {
|
|||||||
return scriptMonsterSpawnService;
|
return scriptMonsterSpawnService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EntityGadget createGadget(int groupId, int blockId, SceneGadget g) {
|
||||||
|
EntityGadget entity = new EntityGadget(getScene(), g.gadget_id, g.pos);
|
||||||
|
|
||||||
|
if (entity.getGadgetData() == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.setBlockId(blockId);
|
||||||
|
entity.setConfigId(g.config_id);
|
||||||
|
entity.setGroupId(groupId);
|
||||||
|
entity.getRotation().set(g.rot);
|
||||||
|
entity.setState(g.state);
|
||||||
|
entity.setPointType(g.point_type);
|
||||||
|
entity.setMetaGadget(g);
|
||||||
|
entity.buildContent();
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
public EntityNPC createNPC(SceneNPC npc, int blockId, int suiteId) {
|
||||||
|
return new EntityNPC(getScene(), npc, blockId, suiteId);
|
||||||
|
}
|
||||||
|
public EntityMonster createMonster(int groupId, int blockId, SceneMonster monster) {
|
||||||
|
if(monster == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
MonsterData data = GameData.getMonsterDataMap().get(monster.monster_id);
|
||||||
|
|
||||||
|
if (data == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(groupId);
|
||||||
|
entity.setBlockId(blockId);
|
||||||
|
entity.setConfigId(monster.config_id);
|
||||||
|
entity.setPoseId(monster.pose_id);
|
||||||
|
|
||||||
|
this.getScriptMonsterSpawnService()
|
||||||
|
.onMonsterCreatedListener.forEach(action -> action.onNotify(entity));
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addEntity(GameEntity gameEntity){
|
||||||
|
getScene().addEntity(gameEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void meetEntities(List<? extends GameEntity> gameEntity){
|
||||||
|
getScene().addEntities(gameEntity, VisionTypeOuterClass.VisionType.VISION_MEET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addEntities(List<? extends GameEntity> gameEntity){
|
||||||
|
getScene().addEntities(gameEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RTree<SceneBlock, Geometry> getBlocksIndex() {
|
||||||
|
return meta.sceneBlockIndex;
|
||||||
|
}
|
||||||
|
public void removeMonstersInGroup(SceneGroup group, SceneSuite suite) {
|
||||||
|
var configSet = suite.sceneMonsters.stream()
|
||||||
|
.map(m -> m.config_id)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
var toRemove = getScene().getEntities().values().stream()
|
||||||
|
.filter(e -> e instanceof EntityMonster)
|
||||||
|
.filter(e -> e.getGroupId() == group.id)
|
||||||
|
.filter(e -> configSet.contains(e.getConfigId()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
getScene().removeEntities(toRemove, VisionTypeOuterClass.VisionType.VISION_MISS);
|
||||||
|
}
|
||||||
|
public void removeGadgetsInGroup(SceneGroup group, SceneSuite suite) {
|
||||||
|
var configSet = suite.sceneGadgets.stream()
|
||||||
|
.map(m -> m.config_id)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
var toRemove = getScene().getEntities().values().stream()
|
||||||
|
.filter(e -> e instanceof EntityGadget)
|
||||||
|
.filter(e -> e.getGroupId() == group.id)
|
||||||
|
.filter(e -> configSet.contains(e.getConfigId()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
getScene().removeEntities(toRemove, VisionTypeOuterClass.VisionType.VISION_MISS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,43 @@
|
|||||||
package emu.grasscutter.scripts;
|
package emu.grasscutter.scripts;
|
||||||
|
|
||||||
import emu.grasscutter.game.dungeons.DungeonChallenge;
|
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
|
||||||
import emu.grasscutter.game.entity.EntityGadget;
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
import emu.grasscutter.game.entity.EntityMonster;
|
import emu.grasscutter.game.entity.EntityMonster;
|
||||||
import emu.grasscutter.game.entity.GameEntity;
|
import emu.grasscutter.game.entity.GameEntity;
|
||||||
|
import emu.grasscutter.game.entity.gadget.GadgetWorktop;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.factory.ChallengeFactory;
|
||||||
import emu.grasscutter.scripts.data.SceneGroup;
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
import emu.grasscutter.scripts.data.SceneRegion;
|
import emu.grasscutter.scripts.data.SceneRegion;
|
||||||
import emu.grasscutter.server.packet.send.PacketCanUseSkillNotify;
|
import emu.grasscutter.server.packet.send.PacketCanUseSkillNotify;
|
||||||
import emu.grasscutter.server.packet.send.PacketGadgetStateNotify;
|
|
||||||
import emu.grasscutter.server.packet.send.PacketWorktopOptionNotify;
|
import emu.grasscutter.server.packet.send.PacketWorktopOptionNotify;
|
||||||
|
import io.netty.util.concurrent.FastThreadLocal;
|
||||||
import org.luaj.vm2.LuaTable;
|
import org.luaj.vm2.LuaTable;
|
||||||
import org.luaj.vm2.LuaValue;
|
import org.luaj.vm2.LuaValue;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class ScriptLib {
|
public class ScriptLib {
|
||||||
public static final Logger logger = LoggerFactory.getLogger(ScriptLib.class);
|
public static final Logger logger = LoggerFactory.getLogger(ScriptLib.class);
|
||||||
private final SceneScriptManager sceneScriptManager;
|
private final FastThreadLocal<SceneScriptManager> sceneScriptManager;
|
||||||
|
private final FastThreadLocal<SceneGroup> currentGroup;
|
||||||
|
public ScriptLib() {
|
||||||
|
this.sceneScriptManager = new FastThreadLocal<>();
|
||||||
|
this.currentGroup = new FastThreadLocal<>();
|
||||||
|
}
|
||||||
|
|
||||||
public ScriptLib(SceneScriptManager sceneScriptManager) {
|
public void setSceneScriptManager(SceneScriptManager sceneScriptManager){
|
||||||
this.sceneScriptManager = sceneScriptManager;
|
this.sceneScriptManager.set(sceneScriptManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeSceneScriptManager(){
|
||||||
|
this.sceneScriptManager.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SceneScriptManager getSceneScriptManager() {
|
public SceneScriptManager getSceneScriptManager() {
|
||||||
return sceneScriptManager;
|
// normally not null
|
||||||
|
return Optional.of(sceneScriptManager.get()).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String printTable(LuaTable table){
|
private String printTable(LuaTable table){
|
||||||
@ -38,7 +49,15 @@ public class ScriptLib {
|
|||||||
sb.append("}");
|
sb.append("}");
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
public void setCurrentGroup(SceneGroup currentGroup){
|
||||||
|
this.currentGroup.set(currentGroup);
|
||||||
|
}
|
||||||
|
public Optional<SceneGroup> getCurrentGroup(){
|
||||||
|
return Optional.of(this.currentGroup.get());
|
||||||
|
}
|
||||||
|
public void removeCurrentGroup(){
|
||||||
|
this.currentGroup.remove();
|
||||||
|
}
|
||||||
public int SetGadgetStateByConfigId(int configId, int gadgetState) {
|
public int SetGadgetStateByConfigId(int configId, int gadgetState) {
|
||||||
logger.debug("[LUA] Call SetGadgetStateByConfigId with {},{}",
|
logger.debug("[LUA] Call SetGadgetStateByConfigId with {},{}",
|
||||||
configId,gadgetState);
|
configId,gadgetState);
|
||||||
@ -49,33 +68,23 @@ public class ScriptLib {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(entity.get() instanceof EntityGadget)) {
|
if (entity.get() instanceof EntityGadget entityGadget) {
|
||||||
return 1;
|
entityGadget.updateState(gadgetState);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityGadget gadget = (EntityGadget) entity.get();
|
return 1;
|
||||||
gadget.setState(gadgetState);
|
|
||||||
|
|
||||||
getSceneScriptManager().getScene().broadcastPacket(new PacketGadgetStateNotify(gadget, gadgetState));
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int SetGroupGadgetStateByConfigId(int groupId, int configId, int gadgetState) {
|
public int SetGroupGadgetStateByConfigId(int groupId, int configId, int gadgetState) {
|
||||||
logger.debug("[LUA] Call SetGroupGadgetStateByConfigId with {},{},{}",
|
logger.debug("[LUA] Call SetGroupGadgetStateByConfigId with {},{},{}",
|
||||||
groupId,configId,gadgetState);
|
groupId,configId,gadgetState);
|
||||||
List<GameEntity> list = getSceneScriptManager().getScene().getEntities().values().stream()
|
|
||||||
.filter(e -> e.getGroupId() == groupId).toList();
|
|
||||||
|
|
||||||
for (GameEntity entity : list) {
|
getSceneScriptManager().getScene().getEntities().values().stream()
|
||||||
if (!(entity instanceof EntityGadget)) {
|
.filter(e -> e.getGroupId() == groupId)
|
||||||
continue;
|
.filter(e -> e instanceof EntityGadget)
|
||||||
}
|
.map(e -> (EntityGadget)e)
|
||||||
|
.forEach(e -> e.updateState(gadgetState));
|
||||||
EntityGadget gadget = (EntityGadget) entity;
|
|
||||||
gadget.setState(gadgetState);
|
|
||||||
|
|
||||||
getSceneScriptManager().getScene().broadcastPacket(new PacketGadgetStateNotify(gadget, gadgetState));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -83,42 +92,47 @@ public class ScriptLib {
|
|||||||
public int SetWorktopOptionsByGroupId(int groupId, int configId, int[] options) {
|
public int SetWorktopOptionsByGroupId(int groupId, int configId, int[] options) {
|
||||||
logger.debug("[LUA] Call SetWorktopOptionsByGroupId with {},{},{}",
|
logger.debug("[LUA] Call SetWorktopOptionsByGroupId with {},{},{}",
|
||||||
groupId,configId,options);
|
groupId,configId,options);
|
||||||
|
|
||||||
Optional<GameEntity> entity = getSceneScriptManager().getScene().getEntities().values().stream()
|
Optional<GameEntity> entity = getSceneScriptManager().getScene().getEntities().values().stream()
|
||||||
.filter(e -> e.getConfigId() == configId && e.getGroupId() == groupId).findFirst();
|
.filter(e -> e.getConfigId() == configId && e.getGroupId() == groupId).findFirst();
|
||||||
|
|
||||||
if (entity.isEmpty()) {
|
|
||||||
|
if (entity.isEmpty() || !(entity.get() instanceof EntityGadget gadget)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(entity.get() instanceof EntityGadget)) {
|
if (!(gadget.getContent() instanceof GadgetWorktop worktop)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityGadget gadget = (EntityGadget) entity.get();
|
worktop.addWorktopOptions(options);
|
||||||
gadget.addWorktopOptions(options);
|
|
||||||
|
|
||||||
getSceneScriptManager().getScene().broadcastPacket(new PacketWorktopOptionNotify(gadget));
|
getSceneScriptManager().getScene().broadcastPacket(new PacketWorktopOptionNotify(gadget));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int SetWorktopOptions(LuaTable table){
|
||||||
|
logger.debug("[LUA] Call SetWorktopOptions with {}", printTable(table));
|
||||||
|
// TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
public int DelWorktopOptionByGroupId(int groupId, int configId, int option) {
|
public int DelWorktopOptionByGroupId(int groupId, int configId, int option) {
|
||||||
logger.debug("[LUA] Call DelWorktopOptionByGroupId with {},{},{}",groupId,configId,option);
|
logger.debug("[LUA] Call DelWorktopOptionByGroupId with {},{},{}",groupId,configId,option);
|
||||||
|
|
||||||
Optional<GameEntity> entity = getSceneScriptManager().getScene().getEntities().values().stream()
|
Optional<GameEntity> entity = getSceneScriptManager().getScene().getEntities().values().stream()
|
||||||
.filter(e -> e.getConfigId() == configId && e.getGroupId() == groupId).findFirst();
|
.filter(e -> e.getConfigId() == configId && e.getGroupId() == groupId).findFirst();
|
||||||
|
|
||||||
if (entity.isEmpty()) {
|
if (entity.isEmpty() || !(entity.get() instanceof EntityGadget gadget)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(entity.get() instanceof EntityGadget)) {
|
if (!(gadget.getContent() instanceof GadgetWorktop worktop)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityGadget gadget = (EntityGadget) entity.get();
|
worktop.removeWorktopOption(option);
|
||||||
gadget.removeWorktopOption(option);
|
|
||||||
|
|
||||||
getSceneScriptManager().getScene().broadcastPacket(new PacketWorktopOptionNotify(gadget));
|
getSceneScriptManager().getScene().broadcastPacket(new PacketWorktopOptionNotify(gadget));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,48 +160,102 @@ public class ScriptLib {
|
|||||||
if (group == null || group.monsters == null) {
|
if (group == null || group.monsters == null) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
var suiteData = group.getSuiteByIndex(suite);
|
||||||
|
if(suiteData == null){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
// avoid spawn wrong monster
|
// avoid spawn wrong monster
|
||||||
if(getSceneScriptManager().getScene().getChallenge() != null)
|
if(getSceneScriptManager().getScene().getChallenge() != null)
|
||||||
if(!getSceneScriptManager().getScene().getChallenge().inProgress() ||
|
if(!getSceneScriptManager().getScene().getChallenge().inProgress() ||
|
||||||
getSceneScriptManager().getScene().getChallenge().getGroup().id != groupId){
|
getSceneScriptManager().getScene().getChallenge().getGroup().id != groupId){
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
this.getSceneScriptManager().spawnMonstersInGroup(group, suite);
|
this.getSceneScriptManager().addGroupSuite(group, suiteData);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
public int GoToGroupSuite(int groupId, int suite) {
|
||||||
|
logger.debug("[LUA] Call GoToGroupSuite with {},{}",
|
||||||
|
groupId,suite);
|
||||||
|
SceneGroup group = getSceneScriptManager().getGroupById(groupId);
|
||||||
|
if (group == null || group.monsters == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
var suiteData = group.getSuiteByIndex(suite);
|
||||||
|
if(suiteData == null){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var suiteItem : group.suites){
|
||||||
|
if(suiteData == suiteItem){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this.getSceneScriptManager().removeGroupSuite(group, suiteItem);
|
||||||
|
}
|
||||||
|
this.getSceneScriptManager().addGroupSuite(group, suiteData);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
public int RemoveExtraGroupSuite(int groupId, int suite) {
|
||||||
|
logger.debug("[LUA] Call RemoveExtraGroupSuite with {},{}",
|
||||||
|
groupId,suite);
|
||||||
|
|
||||||
|
SceneGroup group = getSceneScriptManager().getGroupById(groupId);
|
||||||
|
if (group == null || group.monsters == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
var suiteData = group.getSuiteByIndex(suite);
|
||||||
|
if(suiteData == null){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getSceneScriptManager().removeGroupSuite(group, suiteData);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
public int KillExtraGroupSuite(int groupId, int suite) {
|
||||||
|
logger.debug("[LUA] Call KillExtraGroupSuite with {},{}",
|
||||||
|
groupId,suite);
|
||||||
|
|
||||||
|
SceneGroup group = getSceneScriptManager().getGroupById(groupId);
|
||||||
|
if (group == null || group.monsters == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
var suiteData = group.getSuiteByIndex(suite);
|
||||||
|
if(suiteData == null){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getSceneScriptManager().removeGroupSuite(group, suiteData);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
// param3 (probably time limit for timed dungeons)
|
// param3 (probably time limit for timed dungeons)
|
||||||
public int ActiveChallenge(int challengeId, int challengeIndex, int timeLimitOrGroupId, int groupId, int objectiveKills, int param5) {
|
public int ActiveChallenge(int challengeId, int challengeIndex, int timeLimitOrGroupId, int groupId, int objectiveKills, int param5) {
|
||||||
logger.debug("[LUA] Call ActiveChallenge with {},{},{},{},{},{}",
|
logger.debug("[LUA] Call ActiveChallenge with {},{},{},{},{},{}",
|
||||||
challengeId,challengeIndex,timeLimitOrGroupId,groupId,objectiveKills,param5);
|
challengeId,challengeIndex,timeLimitOrGroupId,groupId,objectiveKills,param5);
|
||||||
|
|
||||||
SceneGroup group = getSceneScriptManager().getGroupById(groupId);
|
var challenge = ChallengeFactory.getChallenge(
|
||||||
var objective = objectiveKills;
|
challengeId,
|
||||||
|
challengeIndex,
|
||||||
|
timeLimitOrGroupId,
|
||||||
|
groupId,
|
||||||
|
objectiveKills,
|
||||||
|
param5,
|
||||||
|
getSceneScriptManager().getScene(),
|
||||||
|
getCurrentGroup().get()
|
||||||
|
);
|
||||||
|
|
||||||
if(group == null){
|
if(challenge == null){
|
||||||
group = getSceneScriptManager().getGroupById(timeLimitOrGroupId);
|
|
||||||
objective = groupId;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (group == null || group.monsters == null) {
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(getSceneScriptManager().getScene().getChallenge() != null &&
|
if(challenge instanceof DungeonChallenge dungeonChallenge){
|
||||||
getSceneScriptManager().getScene().getChallenge().inProgress())
|
// set if tower first stage (6-1)
|
||||||
{
|
dungeonChallenge.setStage(getSceneScriptManager().getVariables().getOrDefault("stage", -1) == 0);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DungeonChallenge challenge = new DungeonChallenge(getSceneScriptManager().getScene(),
|
|
||||||
group, challengeId, challengeIndex, objective);
|
|
||||||
// set if tower first stage (6-1)
|
|
||||||
challenge.setStage(getSceneScriptManager().getVariables().getOrDefault("stage", -1) == 0);
|
|
||||||
|
|
||||||
getSceneScriptManager().getScene().setChallenge(challenge);
|
getSceneScriptManager().getScene().setChallenge(challenge);
|
||||||
|
|
||||||
challenge.start();
|
challenge.start();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -244,7 +312,7 @@ public class ScriptLib {
|
|||||||
|
|
||||||
public int GetRegionEntityCount(LuaTable table) {
|
public int GetRegionEntityCount(LuaTable table) {
|
||||||
logger.debug("[LUA] Call GetRegionEntityCount with {}",
|
logger.debug("[LUA] Call GetRegionEntityCount with {}",
|
||||||
table);
|
printTable(table));
|
||||||
int regionId = table.get("region_eid").toint();
|
int regionId = table.get("region_eid").toint();
|
||||||
int entityType = table.get("entity_type").toint();
|
int entityType = table.get("entity_type").toint();
|
||||||
|
|
||||||
@ -267,12 +335,12 @@ public class ScriptLib {
|
|||||||
// TODO record time
|
// TODO record time
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
public int GetGroupMonsterCount(int var1){
|
public int GetGroupMonsterCount(){
|
||||||
logger.debug("[LUA] Call GetGroupMonsterCount with {}",
|
logger.debug("[LUA] Call GetGroupMonsterCount ");
|
||||||
var1);
|
|
||||||
|
|
||||||
return (int) getSceneScriptManager().getScene().getEntities().values().stream()
|
return (int) getSceneScriptManager().getScene().getEntities().values().stream()
|
||||||
.filter(e -> e instanceof EntityMonster && e.getGroupId() == getSceneScriptManager().getCurrentGroup().id)
|
.filter(e -> e instanceof EntityMonster &&
|
||||||
|
e.getGroupId() == getCurrentGroup().map(sceneGroup -> sceneGroup.id).orElse(-1))
|
||||||
.count();
|
.count();
|
||||||
}
|
}
|
||||||
public int SetMonsterBattleByGroup(int var1, int var2, int var3){
|
public int SetMonsterBattleByGroup(int var1, int var2, int var3){
|
||||||
@ -314,7 +382,7 @@ public class ScriptLib {
|
|||||||
|
|
||||||
var entity = getSceneScriptManager().getScene().getEntityByConfigId(configId.toint());
|
var entity = getSceneScriptManager().getScene().getEntityByConfigId(configId.toint());
|
||||||
if(entity == null){
|
if(entity == null){
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
getSceneScriptManager().getScene().killEntity(entity, 0);
|
getSceneScriptManager().getScene().killEntity(entity, 0);
|
||||||
return 0;
|
return 0;
|
||||||
@ -334,7 +402,11 @@ public class ScriptLib {
|
|||||||
var configId = table.get("config_id").toint();
|
var configId = table.get("config_id").toint();
|
||||||
var delayTime = table.get("delay_time").toint();
|
var delayTime = table.get("delay_time").toint();
|
||||||
|
|
||||||
getSceneScriptManager().spawnMonstersByConfigId(configId, delayTime);
|
if(getCurrentGroup().isEmpty()){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSceneScriptManager().spawnMonstersByConfigId(getCurrentGroup().get(), configId, delayTime);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,8 +425,81 @@ public class ScriptLib {
|
|||||||
printTable(table));
|
printTable(table));
|
||||||
var configId = table.get("config_id").toint();
|
var configId = table.get("config_id").toint();
|
||||||
|
|
||||||
//TODO
|
var group = getCurrentGroup();
|
||||||
|
|
||||||
|
if (group.isEmpty()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gadget = group.get().gadgets.get(configId);
|
||||||
|
var entity = getSceneScriptManager().createGadget(group.get().id, group.get().block_id, gadget);
|
||||||
|
|
||||||
|
getSceneScriptManager().addEntity(entity);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
public int CheckRemainGadgetCountByGroupId(LuaTable table){
|
||||||
|
logger.debug("[LUA] Call CheckRemainGadgetCountByGroupId with {}",
|
||||||
|
printTable(table));
|
||||||
|
var groupId = table.get("group_id").toint();
|
||||||
|
|
||||||
|
var count = getSceneScriptManager().getScene().getEntities().values().stream()
|
||||||
|
.filter(g -> g instanceof EntityGadget entityGadget && entityGadget.getGroupId() == groupId)
|
||||||
|
.count();
|
||||||
|
return (int)count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetGadgetStateByConfigId(int groupId, int configId){
|
||||||
|
logger.debug("[LUA] Call GetGadgetStateByConfigId with {},{}",
|
||||||
|
groupId, configId);
|
||||||
|
|
||||||
|
if(groupId == 0){
|
||||||
|
groupId = getCurrentGroup().get().id;
|
||||||
|
}
|
||||||
|
final int realGroupId = groupId;
|
||||||
|
var gadget = getSceneScriptManager().getScene().getEntities().values().stream()
|
||||||
|
.filter(g -> g instanceof EntityGadget entityGadget && entityGadget.getGroupId() == realGroupId)
|
||||||
|
.filter(g -> g.getConfigId() == configId)
|
||||||
|
.findFirst();
|
||||||
|
if(gadget.isEmpty()){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return ((EntityGadget)gadget.get()).getState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int MarkPlayerAction(int var1, int var2, int var3, int var4){
|
||||||
|
logger.debug("[LUA] Call MarkPlayerAction with {},{},{},{}",
|
||||||
|
var1, var2,var3,var4);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int AddQuestProgress(String var1){
|
||||||
|
logger.debug("[LUA] Call AddQuestProgress with {}",
|
||||||
|
var1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* change the state of gadget
|
||||||
|
*/
|
||||||
|
public int ChangeGroupGadget(LuaTable table){
|
||||||
|
logger.debug("[LUA] Call ChangeGroupGadget with {}",
|
||||||
|
printTable(table));
|
||||||
|
var configId = table.get("config_id").toint();
|
||||||
|
var state = table.get("state").toint();
|
||||||
|
|
||||||
|
var entity = getSceneScriptManager().getScene().getEntityByConfigId(configId);
|
||||||
|
if(entity == null){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity instanceof EntityGadget entityGadget) {
|
||||||
|
entityGadget.updateState(state);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,27 @@
|
|||||||
package emu.grasscutter.scripts;
|
package emu.grasscutter.scripts;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
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 org.luaj.vm2.LuaTable;
|
|
||||||
import org.luaj.vm2.LuaValue;
|
|
||||||
import org.luaj.vm2.lib.OneArgFunction;
|
|
||||||
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
|
|
||||||
import org.luaj.vm2.script.LuajContext;
|
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.game.props.EntityType;
|
import emu.grasscutter.game.props.EntityType;
|
||||||
import emu.grasscutter.scripts.constants.EventType;
|
import emu.grasscutter.scripts.constants.EventType;
|
||||||
import emu.grasscutter.scripts.constants.ScriptGadgetState;
|
import emu.grasscutter.scripts.constants.ScriptGadgetState;
|
||||||
import emu.grasscutter.scripts.constants.ScriptRegionShape;
|
import emu.grasscutter.scripts.constants.ScriptRegionShape;
|
||||||
|
import emu.grasscutter.scripts.data.SceneMeta;
|
||||||
import emu.grasscutter.scripts.serializer.LuaSerializer;
|
import emu.grasscutter.scripts.serializer.LuaSerializer;
|
||||||
import emu.grasscutter.scripts.serializer.Serializer;
|
import emu.grasscutter.scripts.serializer.Serializer;
|
||||||
|
import org.luaj.vm2.LuaTable;
|
||||||
|
import org.luaj.vm2.LuaValue;
|
||||||
|
import org.luaj.vm2.lib.OneArgFunction;
|
||||||
|
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
|
||||||
|
import org.luaj.vm2.script.LuajContext;
|
||||||
|
|
||||||
|
import javax.script.*;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.lang.ref.SoftReference;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class ScriptLoader {
|
public class ScriptLoader {
|
||||||
private static ScriptEngineManager sm;
|
private static ScriptEngineManager sm;
|
||||||
@ -34,8 +29,16 @@ public class ScriptLoader {
|
|||||||
private static ScriptEngineFactory factory;
|
private static ScriptEngineFactory factory;
|
||||||
private static String fileType;
|
private static String fileType;
|
||||||
private static Serializer serializer;
|
private static Serializer serializer;
|
||||||
|
private static ScriptLib scriptLib;
|
||||||
private static Map<String, CompiledScript> scripts = new HashMap<>();
|
private static LuaValue scriptLibLua;
|
||||||
|
/**
|
||||||
|
* suggest GC to remove it if the memory is less
|
||||||
|
*/
|
||||||
|
private static Map<String, SoftReference<CompiledScript>> scriptsCache = new ConcurrentHashMap<>();
|
||||||
|
/**
|
||||||
|
* sceneId - SceneMeta
|
||||||
|
*/
|
||||||
|
private static Map<Integer, SoftReference<SceneMeta>> sceneMetaCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public synchronized static void init() throws Exception {
|
public synchronized static void init() throws Exception {
|
||||||
if (sm != null) {
|
if (sm != null) {
|
||||||
@ -67,6 +70,10 @@ public class ScriptLoader {
|
|||||||
ctx.globals.set("EventType", CoerceJavaToLua.coerce(new EventType())); // TODO - make static class to avoid instantiating a new class every scene
|
ctx.globals.set("EventType", CoerceJavaToLua.coerce(new EventType())); // TODO - make static class to avoid instantiating a new class every scene
|
||||||
ctx.globals.set("GadgetState", CoerceJavaToLua.coerce(new ScriptGadgetState()));
|
ctx.globals.set("GadgetState", CoerceJavaToLua.coerce(new ScriptGadgetState()));
|
||||||
ctx.globals.set("RegionShape", CoerceJavaToLua.coerce(new ScriptRegionShape()));
|
ctx.globals.set("RegionShape", CoerceJavaToLua.coerce(new ScriptRegionShape()));
|
||||||
|
|
||||||
|
scriptLib = new ScriptLib();
|
||||||
|
scriptLibLua = CoerceJavaToLua.coerce(scriptLib);
|
||||||
|
ctx.globals.set("ScriptLib", scriptLibLua);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ScriptEngine getEngine() {
|
public static ScriptEngine getEngine() {
|
||||||
@ -81,25 +88,50 @@ public class ScriptLoader {
|
|||||||
return serializer;
|
return serializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ScriptLib getScriptLib() {
|
||||||
|
return scriptLib;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LuaValue getScriptLibLua() {
|
||||||
|
return scriptLibLua;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Optional<T> tryGet(SoftReference<T> softReference){
|
||||||
|
try{
|
||||||
|
return Optional.ofNullable(softReference.get());
|
||||||
|
}catch (NullPointerException npe){
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
public static CompiledScript getScriptByPath(String path) {
|
public static CompiledScript getScriptByPath(String path) {
|
||||||
CompiledScript sc = scripts.get(path);
|
var sc = tryGet(scriptsCache.get(path));
|
||||||
|
if (sc.isPresent()) {
|
||||||
Grasscutter.getLogger().info("Loaded " + path);
|
return sc.get();
|
||||||
|
}
|
||||||
if (sc == null) {
|
|
||||||
File file = new File(path);
|
Grasscutter.getLogger().info("Loading script " + path);
|
||||||
|
|
||||||
if (!file.exists()) return null;
|
File file = new File(path);
|
||||||
|
|
||||||
try (FileReader fr = new FileReader(file)) {
|
if (!file.exists()) return null;
|
||||||
sc = ((Compilable) getEngine()).compile(fr);
|
|
||||||
scripts.put(path, sc);
|
try (FileReader fr = new FileReader(file)) {
|
||||||
} catch (Exception e) {
|
var script = ((Compilable) getEngine()).compile(fr);
|
||||||
//e.printStackTrace();
|
scriptsCache.put(path, new SoftReference<>(script));
|
||||||
return null;
|
return script;
|
||||||
}
|
} catch (Exception e) {
|
||||||
|
Grasscutter.getLogger().error("Loading script {} failed!", path, e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SceneMeta getSceneMeta(int sceneId) {
|
||||||
|
return tryGet(sceneMetaCache.get(sceneId)).orElseGet(() -> {
|
||||||
|
var instance = SceneMeta.of(sceneId);
|
||||||
|
sceneMetaCache.put(sceneId, new SoftReference<>(instance));
|
||||||
|
return instance;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
28
src/main/java/emu/grasscutter/scripts/ScriptUtils.java
Normal file
28
src/main/java/emu/grasscutter/scripts/ScriptUtils.java
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package emu.grasscutter.scripts;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import org.luaj.vm2.LuaTable;
|
||||||
|
import org.luaj.vm2.LuaValue;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
|
||||||
|
public class ScriptUtils {
|
||||||
|
|
||||||
|
public static HashMap<Object, Object> toMap(LuaTable table) {
|
||||||
|
HashMap<Object, Object> map = new HashMap<>();
|
||||||
|
LuaValue[] rootKeys = table.keys();
|
||||||
|
for (LuaValue k : rootKeys) {
|
||||||
|
if (table.get(k).istable()) {
|
||||||
|
map.put(k, toMap(table.get(k).checktable()));
|
||||||
|
} else {
|
||||||
|
map.put(k, table.get(k));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void print(LuaTable table) {
|
||||||
|
Grasscutter.getLogger().info(toMap(table).toString());
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,9 @@ package emu.grasscutter.scripts.constants;
|
|||||||
|
|
||||||
public class EventType {
|
public class EventType {
|
||||||
public static final int EVENT_NONE = 0;
|
public static final int EVENT_NONE = 0;
|
||||||
|
/**
|
||||||
|
* param1: monster.configId
|
||||||
|
*/
|
||||||
public static final int EVENT_ANY_MONSTER_DIE = 1;
|
public static final int EVENT_ANY_MONSTER_DIE = 1;
|
||||||
public static final int EVENT_ANY_GADGET_DIE = 2;
|
public static final int EVENT_ANY_GADGET_DIE = 2;
|
||||||
public static final int EVENT_VARIABLE_CHANGE = 3;
|
public static final int EVENT_VARIABLE_CHANGE = 3;
|
||||||
|
@ -1,17 +1,81 @@
|
|||||||
package emu.grasscutter.scripts.data;
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
import java.util.List;
|
import com.github.davidmoten.rtreemulti.RTree;
|
||||||
|
import com.github.davidmoten.rtreemulti.geometry.Geometry;
|
||||||
|
import com.github.davidmoten.rtreemulti.geometry.Rectangle;
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.scripts.SceneIndexManager;
|
||||||
|
import emu.grasscutter.scripts.ScriptLoader;
|
||||||
import emu.grasscutter.utils.Position;
|
import emu.grasscutter.utils.Position;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import javax.script.Bindings;
|
||||||
|
import javax.script.CompiledScript;
|
||||||
|
import javax.script.ScriptException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static emu.grasscutter.Configuration.SCRIPT;
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
@Setter
|
||||||
public class SceneBlock {
|
public class SceneBlock {
|
||||||
public int id;
|
public int id;
|
||||||
public Position max;
|
public Position max;
|
||||||
public Position min;
|
public Position min;
|
||||||
public List<SceneGroup> groups;
|
|
||||||
|
public int sceneId;
|
||||||
|
public Map<Integer,SceneGroup> groups;
|
||||||
|
public RTree<SceneGroup, Geometry> sceneGroupIndex;
|
||||||
|
|
||||||
|
private transient boolean loaded; // Not an actual variable in the scripts either
|
||||||
|
|
||||||
|
public boolean isLoaded() {
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoaded(boolean loaded) {
|
||||||
|
this.loaded = loaded;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean contains(Position pos) {
|
public boolean contains(Position pos) {
|
||||||
return pos.getX() <= max.getX() && pos.getX() >= min.getX() &&
|
return pos.getX() <= max.getX() && pos.getX() >= min.getX() &&
|
||||||
pos.getZ() <= max.getZ() && pos.getZ() >= min.getZ();
|
pos.getZ() <= max.getZ() && pos.getZ() >= min.getZ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SceneBlock load(int sceneId, Bindings bindings){
|
||||||
|
if(loaded){
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
this.sceneId = sceneId;
|
||||||
|
setLoaded(true);
|
||||||
|
|
||||||
|
CompiledScript cs = ScriptLoader.getScriptByPath(
|
||||||
|
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_block" + id + "." + ScriptLoader.getScriptType()));
|
||||||
|
|
||||||
|
if (cs == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eval script
|
||||||
|
try {
|
||||||
|
cs.eval(bindings);
|
||||||
|
|
||||||
|
// Set groups
|
||||||
|
groups = ScriptLoader.getSerializer().toList(SceneGroup.class, bindings.get("groups")).stream()
|
||||||
|
.collect(Collectors.toMap(x -> x.id, y -> y));
|
||||||
|
|
||||||
|
groups.values().forEach(g -> g.block_id = id);
|
||||||
|
this.sceneGroupIndex = SceneIndexManager.buildIndex(3, groups.values(), g -> g.pos.toPoint());
|
||||||
|
} catch (ScriptException e) {
|
||||||
|
Grasscutter.getLogger().error("Error loading block " + id + " in scene " + sceneId, e);
|
||||||
|
}
|
||||||
|
Grasscutter.getLogger().info("scene {} block {} is loaded successfully.", sceneId, id);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rectangle toRectangle() {
|
||||||
|
return Rectangle.create(min.toXZDoubleArray(), max.toXZDoubleArray());
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@ToString
|
||||||
|
public class SceneBossChest {
|
||||||
|
public int life_time;
|
||||||
|
public int monster_config_id;
|
||||||
|
public int resin;
|
||||||
|
public int take_num;
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
@Setter
|
||||||
|
public class SceneBusiness {
|
||||||
|
public int type;
|
||||||
|
}
|
@ -1,7 +1,11 @@
|
|||||||
package emu.grasscutter.scripts.data;
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
import emu.grasscutter.utils.Position;
|
import emu.grasscutter.utils.Position;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
@Setter
|
||||||
public class SceneConfig {
|
public class SceneConfig {
|
||||||
public Position vision_anchor;
|
public Position vision_anchor;
|
||||||
public Position born_pos;
|
public Position born_pos;
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
package emu.grasscutter.scripts.data;
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
import emu.grasscutter.utils.Position;
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
public class SceneGadget {
|
@ToString
|
||||||
public int level;
|
@Setter
|
||||||
public int config_id;
|
public class SceneGadget extends SceneObject{
|
||||||
public int gadget_id;
|
public int gadget_id;
|
||||||
public int state;
|
public int state;
|
||||||
public Position pos;
|
public int point_type;
|
||||||
public Position rot;
|
public SceneBossChest boss_chest;
|
||||||
|
public int interact_id;
|
||||||
}
|
}
|
||||||
|
12
src/main/java/emu/grasscutter/scripts/data/SceneGarbage.java
Normal file
12
src/main/java/emu/grasscutter/scripts/data/SceneGarbage.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
@Setter
|
||||||
|
public class SceneGarbage {
|
||||||
|
public List<SceneGadget> gadgets;
|
||||||
|
}
|
@ -1,10 +1,27 @@
|
|||||||
package emu.grasscutter.scripts.data;
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.scripts.ScriptLoader;
|
||||||
import emu.grasscutter.utils.Position;
|
import emu.grasscutter.utils.Position;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import javax.script.Bindings;
|
||||||
|
import javax.script.CompiledScript;
|
||||||
|
import javax.script.ScriptException;
|
||||||
|
|
||||||
|
import org.luaj.vm2.LuaTable;
|
||||||
|
import org.luaj.vm2.LuaValue;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static emu.grasscutter.Configuration.SCRIPT;
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
@Setter
|
||||||
public class SceneGroup {
|
public class SceneGroup {
|
||||||
public transient int block_id; // Not an actual variable in the scripts but we will keep it here for reference
|
public transient int block_id; // Not an actual variable in the scripts but we will keep it here for reference
|
||||||
|
|
||||||
@ -12,27 +29,140 @@ public class SceneGroup {
|
|||||||
public int refresh_id;
|
public int refresh_id;
|
||||||
public Position pos;
|
public Position pos;
|
||||||
|
|
||||||
/**
|
public Map<Integer,SceneMonster> monsters; // <ConfigId, Monster>
|
||||||
* ConfigId - Monster
|
public Map<Integer, SceneGadget> gadgets; // <ConfigId, Gadgets>
|
||||||
*/
|
public Map<String, SceneTrigger> triggers;
|
||||||
public Map<Integer,SceneMonster> monsters;
|
public Map<Integer, SceneNPC> npc; // <NpcId, NPC>
|
||||||
public List<SceneGadget> gadgets;
|
|
||||||
public List<SceneTrigger> triggers;
|
|
||||||
public List<SceneRegion> regions;
|
public List<SceneRegion> regions;
|
||||||
public List<SceneSuite> suites;
|
public List<SceneSuite> suites;
|
||||||
|
public List<SceneVar> variables;
|
||||||
|
|
||||||
|
public SceneBusiness business;
|
||||||
|
public SceneGarbage garbages;
|
||||||
public SceneInitConfig init_config;
|
public SceneInitConfig init_config;
|
||||||
|
|
||||||
private transient boolean isLoaded; // Not an actual variable in the scripts either
|
private transient boolean loaded; // Not an actual variable in the scripts either
|
||||||
|
private transient CompiledScript script;
|
||||||
public boolean isLoaded() {
|
private transient Bindings bindings;
|
||||||
return isLoaded;
|
public static SceneGroup of(int groupId) {
|
||||||
|
var group = new SceneGroup();
|
||||||
|
group.id = groupId;
|
||||||
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean setLoaded(boolean loaded) {
|
public boolean isLoaded() {
|
||||||
return loaded;
|
return loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLoaded(boolean loaded) {
|
||||||
|
this.loaded = loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBusinessType() {
|
||||||
|
return this.business == null ? 0 : this.business.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SceneGadget> getGarbageGadgets() {
|
||||||
|
return this.garbages == null ? null : this.garbages.gadgets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompiledScript getScript() {
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
|
||||||
public SceneSuite getSuiteByIndex(int index) {
|
public SceneSuite getSuiteByIndex(int index) {
|
||||||
return suites.get(index - 1);
|
return suites.get(index - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Bindings getBindings() {
|
||||||
|
return bindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized SceneGroup load(int sceneId){
|
||||||
|
if(loaded){
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
// Set flag here so if there is no script, we dont call this function over and over again.
|
||||||
|
setLoaded(true);
|
||||||
|
|
||||||
|
this.bindings = ScriptLoader.getEngine().createBindings();
|
||||||
|
|
||||||
|
CompiledScript cs = ScriptLoader.getScriptByPath(
|
||||||
|
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_group" + id + "." + ScriptLoader.getScriptType()));
|
||||||
|
|
||||||
|
if (cs == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.script = cs;
|
||||||
|
|
||||||
|
// Eval script
|
||||||
|
try {
|
||||||
|
cs.eval(bindings);
|
||||||
|
|
||||||
|
// Set
|
||||||
|
monsters = ScriptLoader.getSerializer().toList(SceneMonster.class, bindings.get("monsters")).stream()
|
||||||
|
.collect(Collectors.toMap(x -> x.config_id, y -> y));
|
||||||
|
monsters.values().forEach(m -> m.group = this);
|
||||||
|
|
||||||
|
gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, bindings.get("gadgets")).stream()
|
||||||
|
.collect(Collectors.toMap(x -> x.config_id, y -> y));
|
||||||
|
gadgets.values().forEach(m -> m.group = this);
|
||||||
|
|
||||||
|
triggers = ScriptLoader.getSerializer().toList(SceneTrigger.class, bindings.get("triggers")).stream()
|
||||||
|
.collect(Collectors.toMap(x -> x.name, y -> y));
|
||||||
|
triggers.values().forEach(t -> t.currentGroup = this);
|
||||||
|
|
||||||
|
suites = ScriptLoader.getSerializer().toList(SceneSuite.class, bindings.get("suites"));
|
||||||
|
regions = ScriptLoader.getSerializer().toList(SceneRegion.class, bindings.get("regions"));
|
||||||
|
init_config = ScriptLoader.getSerializer().toObject(SceneInitConfig.class, bindings.get("init_config"));
|
||||||
|
|
||||||
|
// Garbages TODO fix properly later
|
||||||
|
Object garbagesValue = bindings.get("garbages");
|
||||||
|
if (garbagesValue != null && garbagesValue instanceof LuaValue garbagesTable) {
|
||||||
|
garbages = new SceneGarbage();
|
||||||
|
if (garbagesTable.checktable().get("gadgets") != LuaValue.NIL) {
|
||||||
|
garbages.gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, garbagesTable.checktable().get("gadgets").checktable());
|
||||||
|
garbages.gadgets.forEach(m -> m.group = this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add variables to suite
|
||||||
|
variables = ScriptLoader.getSerializer().toList(SceneVar.class, bindings.get("variables"));
|
||||||
|
// NPC in groups
|
||||||
|
npc = ScriptLoader.getSerializer().toList(SceneNPC.class, bindings.get("npcs")).stream()
|
||||||
|
.collect(Collectors.toMap(x -> x.npc_id, y -> y));
|
||||||
|
npc.values().forEach(n -> n.group = this);
|
||||||
|
|
||||||
|
// Add monsters and gadgets to suite
|
||||||
|
for (SceneSuite suite : suites) {
|
||||||
|
suite.sceneMonsters = new ArrayList<>(
|
||||||
|
suite.monsters.stream()
|
||||||
|
.filter(monsters::containsKey)
|
||||||
|
.map(monsters::get)
|
||||||
|
.toList()
|
||||||
|
);
|
||||||
|
|
||||||
|
suite.sceneGadgets = new ArrayList<>(
|
||||||
|
suite.gadgets.stream()
|
||||||
|
.filter(gadgets::containsKey)
|
||||||
|
.map(gadgets::get)
|
||||||
|
.toList()
|
||||||
|
);
|
||||||
|
|
||||||
|
suite.sceneTriggers = new ArrayList<>(
|
||||||
|
suite.triggers.stream()
|
||||||
|
.filter(triggers::containsKey)
|
||||||
|
.map(triggers::get)
|
||||||
|
.toList()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (ScriptException e) {
|
||||||
|
Grasscutter.getLogger().error("Error loading group " + id + " in scene " + sceneId, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Grasscutter.getLogger().info("group {} in scene {} is loaded successfully.", id, sceneId);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package emu.grasscutter.scripts.data;
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
import emu.grasscutter.utils.Position;
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
@Setter
|
||||||
public class SceneInitConfig {
|
public class SceneInitConfig {
|
||||||
public int suite;
|
public int suite;
|
||||||
public int end_suite;
|
public int end_suite;
|
||||||
|
75
src/main/java/emu/grasscutter/scripts/data/SceneMeta.java
Normal file
75
src/main/java/emu/grasscutter/scripts/data/SceneMeta.java
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
|
import com.github.davidmoten.rtreemulti.RTree;
|
||||||
|
import com.github.davidmoten.rtreemulti.geometry.Geometry;
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.scripts.SceneIndexManager;
|
||||||
|
import emu.grasscutter.scripts.ScriptLoader;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import javax.script.Bindings;
|
||||||
|
import javax.script.CompiledScript;
|
||||||
|
import javax.script.ScriptException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static emu.grasscutter.Configuration.SCRIPT;
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
@Setter
|
||||||
|
public class SceneMeta {
|
||||||
|
|
||||||
|
public SceneConfig config;
|
||||||
|
public Map<Integer, SceneBlock> blocks;
|
||||||
|
|
||||||
|
public Bindings context;
|
||||||
|
|
||||||
|
public RTree<SceneBlock, Geometry> sceneBlockIndex;
|
||||||
|
|
||||||
|
public static SceneMeta of(int sceneId) {
|
||||||
|
return new SceneMeta().load(sceneId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SceneMeta load(int sceneId){
|
||||||
|
// Get compiled script if cached
|
||||||
|
CompiledScript cs = ScriptLoader.getScriptByPath(
|
||||||
|
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "." + ScriptLoader.getScriptType()));
|
||||||
|
|
||||||
|
if (cs == null) {
|
||||||
|
Grasscutter.getLogger().warn("No script found for scene " + sceneId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create bindings
|
||||||
|
context = ScriptLoader.getEngine().createBindings();
|
||||||
|
|
||||||
|
// Eval script
|
||||||
|
try {
|
||||||
|
cs.eval(context);
|
||||||
|
|
||||||
|
this.config = ScriptLoader.getSerializer().toObject(SceneConfig.class, context.get("scene_config"));
|
||||||
|
|
||||||
|
// TODO optimize later
|
||||||
|
// Create blocks
|
||||||
|
List<Integer> blockIds = ScriptLoader.getSerializer().toList(Integer.class, context.get("blocks"));
|
||||||
|
List<SceneBlock> blocks = ScriptLoader.getSerializer().toList(SceneBlock.class, context.get("block_rects"));
|
||||||
|
|
||||||
|
for (int i = 0; i < blocks.size(); i++) {
|
||||||
|
SceneBlock block = blocks.get(i);
|
||||||
|
block.id = blockIds.get(i);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
this.blocks = blocks.stream().collect(Collectors.toMap(b -> b.id, b -> b));
|
||||||
|
this.sceneBlockIndex = SceneIndexManager.buildIndex(2, blocks, SceneBlock::toRectangle);
|
||||||
|
|
||||||
|
} catch (ScriptException e) {
|
||||||
|
Grasscutter.getLogger().error("Error running script", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Grasscutter.getLogger().info("scene {} metadata is loaded successfully.", sceneId);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,12 @@
|
|||||||
package emu.grasscutter.scripts.data;
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
import emu.grasscutter.utils.Position;
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
public class SceneMonster {
|
@ToString
|
||||||
public int level;
|
@Setter
|
||||||
public int config_id;
|
public class SceneMonster extends SceneObject{
|
||||||
public int monster_id;
|
public int monster_id;
|
||||||
public Position pos;
|
public int pose_id;
|
||||||
public Position rot;
|
public int drop_id;
|
||||||
}
|
}
|
10
src/main/java/emu/grasscutter/scripts/data/SceneNPC.java
Normal file
10
src/main/java/emu/grasscutter/scripts/data/SceneNPC.java
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
@Setter
|
||||||
|
public class SceneNPC extends SceneObject{
|
||||||
|
public int npc_id;
|
||||||
|
}
|
20
src/main/java/emu/grasscutter/scripts/data/SceneObject.java
Normal file
20
src/main/java/emu/grasscutter/scripts/data/SceneObject.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
|
import emu.grasscutter.utils.Position;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
@Setter
|
||||||
|
public class SceneObject {
|
||||||
|
public int level;
|
||||||
|
public int config_id;
|
||||||
|
public int area_id;
|
||||||
|
|
||||||
|
public Position pos;
|
||||||
|
public Position rot;
|
||||||
|
/**
|
||||||
|
* not set by lua
|
||||||
|
*/
|
||||||
|
public transient SceneGroup group;
|
||||||
|
}
|
@ -5,7 +5,12 @@ import emu.grasscutter.scripts.constants.ScriptRegionShape;
|
|||||||
import emu.grasscutter.utils.Position;
|
import emu.grasscutter.utils.Position;
|
||||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
@Setter
|
||||||
public class SceneRegion {
|
public class SceneRegion {
|
||||||
public int config_id;
|
public int config_id;
|
||||||
public int shape;
|
public int shape;
|
||||||
|
@ -2,8 +2,11 @@ package emu.grasscutter.scripts.data;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import emu.grasscutter.utils.Position;
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
@Setter
|
||||||
public class SceneSuite {
|
public class SceneSuite {
|
||||||
public List<Integer> monsters;
|
public List<Integer> monsters;
|
||||||
public List<Integer> gadgets;
|
public List<Integer> gadgets;
|
||||||
@ -12,4 +15,5 @@ public class SceneSuite {
|
|||||||
|
|
||||||
public transient List<SceneMonster> sceneMonsters;
|
public transient List<SceneMonster> sceneMonsters;
|
||||||
public transient List<SceneGadget> sceneGadgets;
|
public transient List<SceneGadget> sceneGadgets;
|
||||||
|
public transient List<SceneTrigger> sceneTriggers;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package emu.grasscutter.scripts.data;
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Setter
|
||||||
public class SceneTrigger {
|
public class SceneTrigger {
|
||||||
public String name;
|
public String name;
|
||||||
public int config_id;
|
public int config_id;
|
||||||
@ -8,6 +11,7 @@ public class SceneTrigger {
|
|||||||
public String condition;
|
public String condition;
|
||||||
public String action;
|
public String action;
|
||||||
|
|
||||||
|
public transient SceneGroup currentGroup;
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if(obj instanceof SceneTrigger sceneTrigger){
|
if(obj instanceof SceneTrigger sceneTrigger){
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
package emu.grasscutter.scripts.data;
|
package emu.grasscutter.scripts.data;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
@Setter
|
||||||
public class SceneVar {
|
public class SceneVar {
|
||||||
public String name;
|
public String name;
|
||||||
public int value;
|
public int value;
|
||||||
|
@ -1,14 +1,28 @@
|
|||||||
package emu.grasscutter.scripts.serializer;
|
package emu.grasscutter.scripts.serializer;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import com.esotericsoftware.reflectasm.ConstructorAccess;
|
||||||
import java.util.ArrayList;
|
import com.esotericsoftware.reflectasm.MethodAccess;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.scripts.ScriptUtils;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.FieldDefaults;
|
||||||
import org.luaj.vm2.LuaTable;
|
import org.luaj.vm2.LuaTable;
|
||||||
import org.luaj.vm2.LuaValue;
|
import org.luaj.vm2.LuaValue;
|
||||||
|
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class LuaSerializer implements Serializer {
|
public class LuaSerializer implements Serializer {
|
||||||
|
|
||||||
|
private final static Map<Class<?>, MethodAccess> methodAccessCache = new ConcurrentHashMap<>();
|
||||||
|
private final static Map<Class<?>, ConstructorAccess<?>> constructorCache = new ConcurrentHashMap<>();
|
||||||
|
private final static Map<Class<?>, Map<String, FieldMeta>> fieldMetaCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> List<T> toList(Class<T> type, Object obj) {
|
public <T> List<T> toList(Class<T> type, Object obj) {
|
||||||
return serializeList(type, (LuaTable) obj);
|
return serializeList(type, (LuaTable) obj);
|
||||||
@ -20,7 +34,11 @@ public class LuaSerializer implements Serializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public <T> List<T> serializeList(Class<T> type, LuaTable table) {
|
public <T> List<T> serializeList(Class<T> type, LuaTable table) {
|
||||||
List<T> list = new ArrayList();
|
List<T> list = new ArrayList<>();
|
||||||
|
|
||||||
|
if (table == null) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
LuaValue[] keys = table.keys();
|
LuaValue[] keys = table.keys();
|
||||||
@ -70,27 +88,38 @@ public class LuaSerializer implements Serializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//noinspection ConfusingArgumentToVarargsMethod
|
if (!methodAccessCache.containsKey(type)) {
|
||||||
object = type.getDeclaredConstructor().newInstance();
|
cacheType(type);
|
||||||
|
}
|
||||||
|
var methodAccess = methodAccessCache.get(type);
|
||||||
|
var fieldMetaMap = fieldMetaCache.get(type);
|
||||||
|
|
||||||
|
object = (T) constructorCache.get(type).newInstance();
|
||||||
|
|
||||||
|
if (table == null) {
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
LuaValue[] keys = table.keys();
|
LuaValue[] keys = table.keys();
|
||||||
for (LuaValue k : keys) {
|
for (LuaValue k : keys) {
|
||||||
try {
|
try {
|
||||||
Field field = object.getClass().getDeclaredField(k.checkjstring());
|
var keyName = k.checkjstring();
|
||||||
|
if(!fieldMetaMap.containsKey(keyName)){
|
||||||
field.setAccessible(true);
|
continue;
|
||||||
|
}
|
||||||
|
var fieldMeta = fieldMetaMap.get(keyName);
|
||||||
LuaValue keyValue = table.get(k);
|
LuaValue keyValue = table.get(k);
|
||||||
|
|
||||||
if (keyValue.istable()) {
|
if (keyValue.istable()) {
|
||||||
field.set(object, serialize(field.getType(), keyValue.checktable()));
|
methodAccess.invoke(object, fieldMeta.index, serialize(fieldMeta.getType(), keyValue.checktable()));
|
||||||
} else if (field.getType().equals(float.class)) {
|
} else if (fieldMeta.getType().equals(float.class)) {
|
||||||
field.setFloat(object, keyValue.tofloat());
|
methodAccess.invoke(object, fieldMeta.index, keyValue.tofloat());
|
||||||
} else if (field.getType().equals(int.class)) {
|
} else if (fieldMeta.getType().equals(int.class)) {
|
||||||
field.setInt(object, keyValue.toint());
|
methodAccess.invoke(object, fieldMeta.index, keyValue.toint());
|
||||||
} else if (field.getType().equals(String.class)) {
|
} else if (fieldMeta.getType().equals(String.class)) {
|
||||||
field.set(object, keyValue.tojstring());
|
methodAccess.invoke(object, fieldMeta.index, keyValue.tojstring());
|
||||||
} else {
|
} else {
|
||||||
field.set(object, keyValue);
|
methodAccess.invoke(object, fieldMeta.index, keyValue.tojstring());
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
//ex.printStackTrace();
|
//ex.printStackTrace();
|
||||||
@ -98,9 +127,64 @@ public class LuaSerializer implements Serializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
Grasscutter.getLogger().info(ScriptUtils.toMap(table).toString());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T> Map<String, FieldMeta> cacheType(Class<T> type){
|
||||||
|
if(fieldMetaCache.containsKey(type)) {
|
||||||
|
return fieldMetaCache.get(type);
|
||||||
|
}
|
||||||
|
if(!constructorCache.containsKey(type)){
|
||||||
|
constructorCache.putIfAbsent(type, ConstructorAccess.get(type));
|
||||||
|
}
|
||||||
|
var methodAccess = Optional.ofNullable(methodAccessCache.get(type)).orElse(MethodAccess.get(type));
|
||||||
|
methodAccessCache.putIfAbsent(type, methodAccess);
|
||||||
|
|
||||||
|
var fieldMetaMap = new HashMap<String, FieldMeta>();
|
||||||
|
var methodNameSet = new HashSet<>(Arrays.stream(methodAccess.getMethodNames()).toList());
|
||||||
|
|
||||||
|
Arrays.stream(type.getDeclaredFields())
|
||||||
|
.filter(field -> methodNameSet.contains(getSetterName(field.getName())))
|
||||||
|
.forEach(field -> {
|
||||||
|
var setter = getSetterName(field.getName());
|
||||||
|
var index = methodAccess.getIndex(setter);
|
||||||
|
fieldMetaMap.put(field.getName(), new FieldMeta(field.getName(), setter, index, field.getType()));
|
||||||
|
});
|
||||||
|
|
||||||
|
Arrays.stream(type.getFields())
|
||||||
|
.filter(field -> !fieldMetaMap.containsKey(field.getName()))
|
||||||
|
.filter(field -> methodNameSet.contains(getSetterName(field.getName())))
|
||||||
|
.forEach(field -> {
|
||||||
|
var setter = getSetterName(field.getName());
|
||||||
|
var index = methodAccess.getIndex(setter);
|
||||||
|
fieldMetaMap.put(field.getName(), new FieldMeta(field.getName(), setter, index, field.getType()));
|
||||||
|
});
|
||||||
|
|
||||||
|
fieldMetaCache.put(type, fieldMetaMap);
|
||||||
|
return fieldMetaMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSetterName(String fieldName){
|
||||||
|
if(fieldName == null || fieldName.length() == 0){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if(fieldName.length() == 1){
|
||||||
|
return "set" + fieldName.toUpperCase();
|
||||||
|
}
|
||||||
|
return "set" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
|
static class FieldMeta{
|
||||||
|
String name;
|
||||||
|
String setter;
|
||||||
|
int index;
|
||||||
|
Class<?> type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,9 @@ import java.util.List;
|
|||||||
public class ScriptMonsterSpawnService {
|
public class ScriptMonsterSpawnService {
|
||||||
|
|
||||||
private final SceneScriptManager sceneScriptManager;
|
private final SceneScriptManager sceneScriptManager;
|
||||||
private final List<ScriptMonsterListener> onMonsterCreatedListener = new ArrayList<>();
|
public final List<ScriptMonsterListener> onMonsterCreatedListener = new ArrayList<>();
|
||||||
|
|
||||||
private final List<ScriptMonsterListener> onMonsterDeadListener = new ArrayList<>();
|
public final List<ScriptMonsterListener> onMonsterDeadListener = new ArrayList<>();
|
||||||
|
|
||||||
public ScriptMonsterSpawnService(SceneScriptManager sceneScriptManager){
|
public ScriptMonsterSpawnService(SceneScriptManager sceneScriptManager){
|
||||||
this.sceneScriptManager = sceneScriptManager;
|
this.sceneScriptManager = sceneScriptManager;
|
||||||
@ -39,40 +39,5 @@ public class ScriptMonsterSpawnService {
|
|||||||
public void onMonsterDead(EntityMonster entityMonster){
|
public void onMonsterDead(EntityMonster entityMonster){
|
||||||
onMonsterDeadListener.forEach(l -> l.onNotify(entityMonster));
|
onMonsterDeadListener.forEach(l -> l.onNotify(entityMonster));
|
||||||
}
|
}
|
||||||
public void spawnMonster(int groupId, SceneMonster monster) {
|
|
||||||
if(monster == null){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MonsterData data = GameData.getMonsterDataMap().get(monster.monster_id);
|
|
||||||
|
|
||||||
if (data == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate level
|
|
||||||
int level = monster.level;
|
|
||||||
|
|
||||||
if (sceneScriptManager.getScene().getDungeonData() != null) {
|
|
||||||
level = sceneScriptManager.getScene().getDungeonData().getShowLevel();
|
|
||||||
} else if (sceneScriptManager.getScene().getWorld().getWorldLevel() > 0) {
|
|
||||||
WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(sceneScriptManager.getScene().getWorld().getWorldLevel());
|
|
||||||
|
|
||||||
if (worldLevelData != null) {
|
|
||||||
level = worldLevelData.getMonsterLevel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spawn mob
|
|
||||||
EntityMonster entity = new EntityMonster(sceneScriptManager.getScene(), data, monster.pos, level);
|
|
||||||
entity.getRotation().set(monster.rot);
|
|
||||||
entity.setGroupId(groupId);
|
|
||||||
entity.setConfigId(monster.config_id);
|
|
||||||
|
|
||||||
onMonsterCreatedListener.forEach(action -> action.onNotify(entity));
|
|
||||||
|
|
||||||
sceneScriptManager.getScene().addEntity(entity);
|
|
||||||
|
|
||||||
sceneScriptManager.callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(entity.getConfigId()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ public class ScriptMonsterTideService {
|
|||||||
this.sceneScriptManager.getScriptMonsterSpawnService().addMonsterDeadListener(onMonsterDead);
|
this.sceneScriptManager.getScriptMonsterSpawnService().addMonsterDeadListener(onMonsterDead);
|
||||||
// spawn the first turn
|
// spawn the first turn
|
||||||
for (int i = 0; i < this.monsterSceneLimit; i++) {
|
for (int i = 0; i < this.monsterSceneLimit; i++) {
|
||||||
this.sceneScriptManager.getScriptMonsterSpawnService().spawnMonster(group.id, getNextMonster());
|
sceneScriptManager.addEntity(this.sceneScriptManager.createMonster(group.id, group.block_id, getNextMonster()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ public class ScriptMonsterTideService {
|
|||||||
monsterKillCount.incrementAndGet();
|
monsterKillCount.incrementAndGet();
|
||||||
if (monsterTideCount.get() > 0) {
|
if (monsterTideCount.get() > 0) {
|
||||||
// add more
|
// add more
|
||||||
sceneScriptManager.getScriptMonsterSpawnService().spawnMonster(currentGroup.id, getNextMonster());
|
sceneScriptManager.addEntity(sceneScriptManager.createMonster(currentGroup.id, currentGroup.block_id, getNextMonster()));
|
||||||
}
|
}
|
||||||
// spawn the last turn of monsters
|
// spawn the last turn of monsters
|
||||||
// fix the 5-2
|
// fix the 5-2
|
||||||
|
@ -19,6 +19,7 @@ import emu.grasscutter.game.quest.ServerQuestHandler;
|
|||||||
import emu.grasscutter.game.shop.ShopManager;
|
import emu.grasscutter.game.shop.ShopManager;
|
||||||
import emu.grasscutter.game.tower.TowerScheduleManager;
|
import emu.grasscutter.game.tower.TowerScheduleManager;
|
||||||
import emu.grasscutter.game.world.World;
|
import emu.grasscutter.game.world.World;
|
||||||
|
import emu.grasscutter.game.world.WorldDataManager;
|
||||||
import emu.grasscutter.net.packet.PacketHandler;
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
|
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
|
||||||
import emu.grasscutter.server.event.types.ServerEvent;
|
import emu.grasscutter.server.event.types.ServerEvent;
|
||||||
@ -55,25 +56,15 @@ public final class GameServer extends KcpServer {
|
|||||||
private final CommandMap commandMap;
|
private final CommandMap commandMap;
|
||||||
private final TaskMap taskMap;
|
private final TaskMap taskMap;
|
||||||
private final DropManager dropManager;
|
private final DropManager dropManager;
|
||||||
|
private final WorldDataManager worldDataManager;
|
||||||
|
|
||||||
private final CombineManger combineManger;
|
private final CombineManger combineManger;
|
||||||
private final TowerScheduleManager towerScheduleManager;
|
private final TowerScheduleManager towerScheduleManager;
|
||||||
|
|
||||||
private static InetSocketAddress getAdapterInetSocketAddress(){
|
|
||||||
InetSocketAddress inetSocketAddress;
|
|
||||||
if(GAME_INFO.bindAddress.equals("")){
|
|
||||||
inetSocketAddress=new InetSocketAddress(GAME_INFO.bindPort);
|
|
||||||
}else{
|
|
||||||
inetSocketAddress=new InetSocketAddress(
|
|
||||||
GAME_INFO.bindAddress,
|
|
||||||
GAME_INFO.bindPort
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return inetSocketAddress;
|
|
||||||
}
|
|
||||||
public GameServer() {
|
public GameServer() {
|
||||||
this(getAdapterInetSocketAddress());
|
this(getAdapterInetSocketAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameServer(InetSocketAddress address) {
|
public GameServer(InetSocketAddress address) {
|
||||||
ChannelConfig channelConfig = new ChannelConfig();
|
ChannelConfig channelConfig = new ChannelConfig();
|
||||||
channelConfig.nodelay(true,40,2,true);
|
channelConfig.nodelay(true,40,2,true);
|
||||||
@ -104,7 +95,7 @@ public final class GameServer extends KcpServer {
|
|||||||
this.expeditionManager = new ExpeditionManager(this);
|
this.expeditionManager = new ExpeditionManager(this);
|
||||||
this.combineManger = new CombineManger(this);
|
this.combineManger = new CombineManger(this);
|
||||||
this.towerScheduleManager = new TowerScheduleManager(this);
|
this.towerScheduleManager = new TowerScheduleManager(this);
|
||||||
|
this.worldDataManager = new WorldDataManager(this);
|
||||||
// Hook into shutdown event.
|
// Hook into shutdown event.
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
|
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
|
||||||
}
|
}
|
||||||
@ -173,11 +164,27 @@ public final class GameServer extends KcpServer {
|
|||||||
return towerScheduleManager;
|
return towerScheduleManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WorldDataManager getWorldDataManager() {
|
||||||
|
return worldDataManager;
|
||||||
|
}
|
||||||
|
|
||||||
public TaskMap getTaskMap() {
|
public TaskMap getTaskMap() {
|
||||||
return this.taskMap;
|
return this.taskMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static InetSocketAddress getAdapterInetSocketAddress(){
|
||||||
|
InetSocketAddress inetSocketAddress;
|
||||||
|
if(GAME_INFO.bindAddress.equals("")){
|
||||||
|
inetSocketAddress=new InetSocketAddress(GAME_INFO.bindPort);
|
||||||
|
}else{
|
||||||
|
inetSocketAddress=new InetSocketAddress(
|
||||||
|
GAME_INFO.bindAddress,
|
||||||
|
GAME_INFO.bindPort
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return inetSocketAddress;
|
||||||
|
}
|
||||||
|
|
||||||
public void registerPlayer(Player player) {
|
public void registerPlayer(Player player) {
|
||||||
getPlayers().put(player.getUid(), player);
|
getPlayers().put(player.getUid(), player);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package emu.grasscutter.server.game;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.Grasscutter.ServerDebugMode;
|
import emu.grasscutter.Grasscutter.ServerDebugMode;
|
||||||
import emu.grasscutter.game.Account;
|
import emu.grasscutter.game.Account;
|
||||||
@ -17,8 +16,9 @@ import emu.grasscutter.utils.FileUtils;
|
|||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import static emu.grasscutter.Configuration.*;
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
import static emu.grasscutter.Configuration.*;
|
||||||
|
|
||||||
public class GameSession implements GameSessionManager.KcpChannel {
|
public class GameSession implements GameSessionManager.KcpChannel {
|
||||||
private final GameServer server;
|
private final GameServer server;
|
||||||
|
@ -13,7 +13,7 @@ public class HandlerGadgetInteractReq extends PacketHandler {
|
|||||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
GadgetInteractReq req = GadgetInteractReq.parseFrom(payload);
|
GadgetInteractReq req = GadgetInteractReq.parseFrom(payload);
|
||||||
|
|
||||||
session.getPlayer().interactWith(req.getGadgetEntityId(), req);
|
session.getPlayer().interactWith(req.getGadgetEntityId(), req.getOpType());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.GetInvestigationMonsterReqOuterClass;
|
||||||
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketGetInvestigationMonsterRsp;
|
||||||
|
|
||||||
|
@Opcodes(PacketOpcodes.GetInvestigationMonsterReq)
|
||||||
|
public class HandlerGetInvestigationMonsterReq extends PacketHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
|
var req = GetInvestigationMonsterReqOuterClass.GetInvestigationMonsterReq.parseFrom(payload);
|
||||||
|
|
||||||
|
session.send(new PacketGetInvestigationMonsterRsp(req.getCityIdListList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,13 +1,13 @@
|
|||||||
package emu.grasscutter.server.packet.send;
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
import emu.grasscutter.game.dungeons.DungeonChallenge;
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.ChallengeDataNotifyOuterClass.ChallengeDataNotify;
|
import emu.grasscutter.net.proto.ChallengeDataNotifyOuterClass.ChallengeDataNotify;
|
||||||
|
|
||||||
public class PacketChallengeDataNotify extends BasePacket {
|
public class PacketChallengeDataNotify extends BasePacket {
|
||||||
|
|
||||||
public PacketChallengeDataNotify(DungeonChallenge challenge, int index, int value) {
|
public PacketChallengeDataNotify(WorldChallenge challenge, int index, int value) {
|
||||||
super(PacketOpcodes.ChallengeDataNotify);
|
super(PacketOpcodes.ChallengeDataNotify);
|
||||||
|
|
||||||
ChallengeDataNotify proto = ChallengeDataNotify.newBuilder()
|
ChallengeDataNotify proto = ChallengeDataNotify.newBuilder()
|
||||||
|
@ -1,21 +1,20 @@
|
|||||||
package emu.grasscutter.server.packet.send;
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
import emu.grasscutter.game.dungeons.DungeonChallenge;
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.DungeonChallengeBeginNotifyOuterClass.DungeonChallengeBeginNotify;
|
import emu.grasscutter.net.proto.DungeonChallengeBeginNotifyOuterClass.DungeonChallengeBeginNotify;
|
||||||
|
|
||||||
public class PacketDungeonChallengeBeginNotify extends BasePacket {
|
public class PacketDungeonChallengeBeginNotify extends BasePacket {
|
||||||
|
|
||||||
public PacketDungeonChallengeBeginNotify(DungeonChallenge challenge) {
|
public PacketDungeonChallengeBeginNotify(WorldChallenge challenge) {
|
||||||
super(PacketOpcodes.DungeonChallengeBeginNotify, true);
|
super(PacketOpcodes.DungeonChallengeBeginNotify, true);
|
||||||
|
|
||||||
DungeonChallengeBeginNotify proto = DungeonChallengeBeginNotify.newBuilder()
|
DungeonChallengeBeginNotify proto = DungeonChallengeBeginNotify.newBuilder()
|
||||||
.setChallengeId(challenge.getChallengeId())
|
.setChallengeId(challenge.getChallengeId())
|
||||||
.setChallengeIndex(challenge.getChallengeIndex())
|
.setChallengeIndex(challenge.getChallengeIndex())
|
||||||
.setGroupId(challenge.getGroup().id)
|
.setGroupId(challenge.getGroup().id)
|
||||||
.addParamList(challenge.getObjective())
|
.addAllParamList(challenge.getParamList())
|
||||||
.addParamList(challenge.getTimeLimit())
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
this.setData(proto);
|
this.setData(proto);
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package emu.grasscutter.server.packet.send;
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
import emu.grasscutter.game.dungeons.DungeonChallenge;
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.DungeonChallengeFinishNotifyOuterClass.DungeonChallengeFinishNotify;
|
import emu.grasscutter.net.proto.DungeonChallengeFinishNotifyOuterClass.DungeonChallengeFinishNotify;
|
||||||
|
|
||||||
public class PacketDungeonChallengeFinishNotify extends BasePacket {
|
public class PacketDungeonChallengeFinishNotify extends BasePacket {
|
||||||
|
|
||||||
public PacketDungeonChallengeFinishNotify(DungeonChallenge challenge) {
|
public PacketDungeonChallengeFinishNotify(WorldChallenge challenge) {
|
||||||
super(PacketOpcodes.DungeonChallengeFinishNotify, true);
|
super(PacketOpcodes.DungeonChallengeFinishNotify, true);
|
||||||
|
|
||||||
DungeonChallengeFinishNotify proto = DungeonChallengeFinishNotify.newBuilder()
|
DungeonChallengeFinishNotify proto = DungeonChallengeFinishNotify.newBuilder()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package emu.grasscutter.server.packet.send;
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
import emu.grasscutter.game.dungeons.DungeonChallenge;
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.DungeonSettleNotifyOuterClass.DungeonSettleNotify;
|
import emu.grasscutter.net.proto.DungeonSettleNotifyOuterClass.DungeonSettleNotify;
|
||||||
@ -9,7 +9,7 @@ import emu.grasscutter.net.proto.TowerLevelEndNotifyOuterClass.TowerLevelEndNoti
|
|||||||
|
|
||||||
public class PacketDungeonSettleNotify extends BasePacket {
|
public class PacketDungeonSettleNotify extends BasePacket {
|
||||||
|
|
||||||
public PacketDungeonSettleNotify(DungeonChallenge challenge) {
|
public PacketDungeonSettleNotify(WorldChallenge challenge) {
|
||||||
super(PacketOpcodes.DungeonSettleNotify);
|
super(PacketOpcodes.DungeonSettleNotify);
|
||||||
|
|
||||||
DungeonSettleNotify proto = DungeonSettleNotify.newBuilder()
|
DungeonSettleNotify proto = DungeonSettleNotify.newBuilder()
|
||||||
@ -22,10 +22,10 @@ public class PacketDungeonSettleNotify extends BasePacket {
|
|||||||
this.setData(proto);
|
this.setData(proto);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PacketDungeonSettleNotify(DungeonChallenge challenge,
|
public PacketDungeonSettleNotify(WorldChallenge challenge,
|
||||||
boolean canJump,
|
boolean canJump,
|
||||||
boolean hasNextLevel,
|
boolean hasNextLevel,
|
||||||
int nextFloorId
|
int nextFloorId
|
||||||
) {
|
) {
|
||||||
super(PacketOpcodes.DungeonSettleNotify);
|
super(PacketOpcodes.DungeonSettleNotify);
|
||||||
|
|
||||||
|
@ -4,20 +4,27 @@ import emu.grasscutter.game.entity.EntityBaseGadget;
|
|||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.GadgetInteractRspOuterClass.GadgetInteractRsp;
|
import emu.grasscutter.net.proto.GadgetInteractRspOuterClass.GadgetInteractRsp;
|
||||||
|
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||||
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
||||||
import emu.grasscutter.net.proto.RetcodeOuterClass;
|
import emu.grasscutter.net.proto.RetcodeOuterClass;
|
||||||
|
|
||||||
public class PacketGadgetInteractRsp extends BasePacket {
|
public class PacketGadgetInteractRsp extends BasePacket {
|
||||||
public PacketGadgetInteractRsp(EntityBaseGadget gadget, InteractType interact) {
|
public PacketGadgetInteractRsp(EntityBaseGadget gadget, InteractType interact) {
|
||||||
|
this(gadget, interact, null);
|
||||||
|
}
|
||||||
|
public PacketGadgetInteractRsp(EntityBaseGadget gadget, InteractType interact, InterOpTypeOuterClass.InterOpType opType) {
|
||||||
super(PacketOpcodes.GadgetInteractRsp);
|
super(PacketOpcodes.GadgetInteractRsp);
|
||||||
|
|
||||||
GadgetInteractRsp proto = GadgetInteractRsp.newBuilder()
|
var proto = GadgetInteractRsp.newBuilder()
|
||||||
.setGadgetEntityId(gadget.getId())
|
.setGadgetEntityId(gadget.getId())
|
||||||
.setInteractType(interact)
|
.setInteractType(interact)
|
||||||
.setGadgetId(gadget.getGadgetId())
|
.setGadgetId(gadget.getGadgetId());
|
||||||
.build();
|
|
||||||
|
|
||||||
this.setData(proto);
|
if(opType != null){
|
||||||
|
proto.setOpType(opType);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setData(proto.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public PacketGadgetInteractRsp() {
|
public PacketGadgetInteractRsp() {
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.GetActivityInfoRspOuterClass;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PacketGetInvestigationMonsterRsp extends BasePacket {
|
||||||
|
|
||||||
|
public PacketGetInvestigationMonsterRsp(List<Integer> cityIdListList) {
|
||||||
|
super(PacketOpcodes.GetInvestigationMonsterRsp);
|
||||||
|
|
||||||
|
var resp = GetActivityInfoRspOuterClass.GetActivityInfoRsp.newBuilder();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.setData(resp.build());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.entity.EntityNPC;
|
||||||
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.GroupSuiteNotifyOuterClass;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PacketGroupSuiteNotify extends BasePacket {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* control which npc suite is loaded
|
||||||
|
*/
|
||||||
|
public PacketGroupSuiteNotify(List<EntityNPC> list) {
|
||||||
|
super(PacketOpcodes.GroupSuiteNotify);
|
||||||
|
|
||||||
|
var proto = GroupSuiteNotifyOuterClass.GroupSuiteNotify.newBuilder();
|
||||||
|
|
||||||
|
list.forEach(item -> proto.putGroupMap(item.getGroupId(), item.getSuiteId()));
|
||||||
|
|
||||||
|
this.setData(proto);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,23 @@
|
|||||||
package emu.grasscutter.server.packet.send;
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
import emu.grasscutter.game.avatar.Avatar;
|
|
||||||
import emu.grasscutter.game.entity.GameEntity;
|
import emu.grasscutter.game.entity.GameEntity;
|
||||||
import emu.grasscutter.game.props.LifeState;
|
import emu.grasscutter.game.props.LifeState;
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.LifeStateChangeNotifyOuterClass.LifeStateChangeNotify;
|
import emu.grasscutter.net.proto.LifeStateChangeNotifyOuterClass.LifeStateChangeNotify;
|
||||||
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
|
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
|
||||||
import emu.grasscutter.net.proto.ServerBuffOuterClass.ServerBuff;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class PacketLifeStateChangeNotify extends BasePacket {
|
public class PacketLifeStateChangeNotify extends BasePacket {
|
||||||
|
public PacketLifeStateChangeNotify(GameEntity target, LifeState lifeState) {
|
||||||
|
super(PacketOpcodes.LifeStateChangeNotify);
|
||||||
|
|
||||||
|
LifeStateChangeNotify proto = LifeStateChangeNotify.newBuilder()
|
||||||
|
.setEntityId(target.getId())
|
||||||
|
.setLifeState(lifeState.getValue())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
this.setData(proto);
|
||||||
|
}
|
||||||
public PacketLifeStateChangeNotify(GameEntity attacker, GameEntity target, LifeState lifeState) {
|
public PacketLifeStateChangeNotify(GameEntity attacker, GameEntity target, LifeState lifeState) {
|
||||||
super(PacketOpcodes.LifeStateChangeNotify);
|
super(PacketOpcodes.LifeStateChangeNotify);
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ public class PacketSceneEntityAppearNotify extends BasePacket {
|
|||||||
this(player.getTeamManager().getCurrentAvatarEntity());
|
this(player.getTeamManager().getCurrentAvatarEntity());
|
||||||
}
|
}
|
||||||
|
|
||||||
public PacketSceneEntityAppearNotify(Collection<GameEntity> entities, VisionType visionType) {
|
public PacketSceneEntityAppearNotify(Collection<? extends GameEntity> entities, VisionType visionType) {
|
||||||
super(PacketOpcodes.SceneEntityAppearNotify, true);
|
super(PacketOpcodes.SceneEntityAppearNotify, true);
|
||||||
|
|
||||||
SceneEntityAppearNotify.Builder proto = SceneEntityAppearNotify.newBuilder()
|
SceneEntityAppearNotify.Builder proto = SceneEntityAppearNotify.newBuilder()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package emu.grasscutter.server.packet.send;
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
import emu.grasscutter.game.entity.EntityGadget;
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
|
import emu.grasscutter.game.entity.gadget.GadgetWorktop;
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.WorktopOptionNotifyOuterClass.WorktopOptionNotify;
|
import emu.grasscutter.net.proto.WorktopOptionNotifyOuterClass.WorktopOptionNotify;
|
||||||
@ -13,8 +14,8 @@ public class PacketWorktopOptionNotify extends BasePacket {
|
|||||||
WorktopOptionNotify.Builder proto = WorktopOptionNotify.newBuilder()
|
WorktopOptionNotify.Builder proto = WorktopOptionNotify.newBuilder()
|
||||||
.setGadgetEntityId(gadget.getId());
|
.setGadgetEntityId(gadget.getId());
|
||||||
|
|
||||||
if (gadget.getWorktopOptions() != null) {
|
if (gadget.getContent() instanceof GadgetWorktop worktop) {
|
||||||
proto.addAllOptionList(gadget.getWorktopOptions());
|
proto.addAllOptionList(worktop.getWorktopOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setData(proto);
|
this.setData(proto);
|
||||||
|
@ -137,6 +137,9 @@ public class ConfigContainer {
|
|||||||
public int bindPort = 22102;
|
public int bindPort = 22102;
|
||||||
/* This is the port used in the default region. */
|
/* This is the port used in the default region. */
|
||||||
public int accessPort = 0;
|
public int accessPort = 0;
|
||||||
|
/* Entities within a certain range will be loaded for the player */
|
||||||
|
public int loadEntitiesForPlayerRange = 100;
|
||||||
|
public boolean enableScriptInBigWorld = false;
|
||||||
public boolean enableConsole = true;
|
public boolean enableConsole = true;
|
||||||
public GameOptions gameOptions = new GameOptions();
|
public GameOptions gameOptions = new GameOptions();
|
||||||
public JoinOptions joinOptions = new JoinOptions();
|
public JoinOptions joinOptions = new JoinOptions();
|
||||||
|
@ -9,6 +9,7 @@ import java.net.URI;
|
|||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -3,7 +3,7 @@ package emu.grasscutter.utils;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import com.github.davidmoten.rtreemulti.geometry.Point;
|
||||||
import dev.morphia.annotations.Entity;
|
import dev.morphia.annotations.Entity;
|
||||||
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
|
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
|
||||||
|
|
||||||
@ -162,4 +162,20 @@ public class Position implements Serializable {
|
|||||||
.setZ(this.getZ())
|
.setZ(this.getZ())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
public Point toPoint(){
|
||||||
|
return Point.create(x,y,z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To XYZ array for Spatial Index
|
||||||
|
*/
|
||||||
|
public double[] toDoubleArray(){
|
||||||
|
return new double[]{ x, y, z};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* To XZ array for Spatial Index (Blocks)
|
||||||
|
*/
|
||||||
|
public double[] toXZDoubleArray(){
|
||||||
|
return new double[]{x, z};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
135
src/main/resources/defaults/data/ChestReward.json
Normal file
135
src/main/resources/defaults/data/ChestReward.json
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"objNames" : [
|
||||||
|
"SceneObj_Chest_Default_Lv1",
|
||||||
|
"SceneObj_Chest_Locked_Lv1",
|
||||||
|
"SceneObj_Chest_Bramble_Lv1",
|
||||||
|
"SceneObj_Chest_Frozen_Lv1",
|
||||||
|
"SceneObj_Chest_Rock_Lv1",
|
||||||
|
"SceneObj_EssenceChest_Default_Lv1",
|
||||||
|
"SceneObj_EssenceChest_Locked_Lv1"
|
||||||
|
],
|
||||||
|
"advExp" : 10,
|
||||||
|
"resin" : 0,
|
||||||
|
"mora" : 257,
|
||||||
|
"sigil" : 1,
|
||||||
|
"content" : [
|
||||||
|
{
|
||||||
|
"itemId" : 104011,
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 104001,
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"randomCount": 4,
|
||||||
|
"randomContent": [
|
||||||
|
{
|
||||||
|
"itemId" : 11101,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 11201,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 12101,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 12201,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 13101,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 13201,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 14101,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 14201,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 15101,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 15201,
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"objNames" : [
|
||||||
|
"SceneObj_Chest_Default_Lv2",
|
||||||
|
"SceneObj_Chest_Locked_Lv2",
|
||||||
|
"SceneObj_Chest_Bramble_Lv2",
|
||||||
|
"SceneObj_Chest_Frozen_Lv2"
|
||||||
|
],
|
||||||
|
"advExp" : 20,
|
||||||
|
"resin" : 2,
|
||||||
|
"mora" : 756,
|
||||||
|
"sigil" : 2,
|
||||||
|
"content" : [
|
||||||
|
{
|
||||||
|
"itemId" : 104012,
|
||||||
|
"count": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 104002,
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"randomCount": 4,
|
||||||
|
"randomContent": [
|
||||||
|
{
|
||||||
|
"itemId" : 11201,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 11301,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 12201,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 12301,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 13201,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 13301,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 14201,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 14301,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 15201,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemId" : 15301,
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
Loading…
Reference in New Issue
Block a user