Skip to content
Snippets Groups Projects
Commit 717c2d1d authored by Akka's avatar Akka Committed by Melledy
Browse files

Support Boss Chest

parent 54294698
Branches
No related merge requests found
Showing
with 271 additions and 88 deletions
...@@ -79,6 +79,7 @@ public class GameData { ...@@ -79,6 +79,7 @@ public class GameData {
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<InvestigationMonsterData> investigationMonsterDataMap = new Int2ObjectOpenHashMap<>();
// Cache // Cache
private static Map<Integer, List<Integer>> fetters = new HashMap<>(); private static Map<Integer, List<Integer>> fetters = new HashMap<>();
...@@ -352,4 +353,8 @@ public class GameData { ...@@ -352,4 +353,8 @@ public class GameData {
public static Int2ObjectMap<GatherData> getGatherDataMap() { public static Int2ObjectMap<GatherData> getGatherDataMap() {
return gatherDataMap; return gatherDataMap;
} }
public static Int2ObjectMap<InvestigationMonsterData> getInvestigationMonsterDataMap() {
return investigationMonsterDataMap;
}
} }
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();
}
}
...@@ -125,7 +125,9 @@ public class DungeonChallenge { ...@@ -125,7 +125,9 @@ public class DungeonChallenge {
if (this.isSuccess()) { if (this.isSuccess()) {
// Call success script event // Call success script event
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_SUCCESS, null); this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_SUCCESS,
// TODO record the time in PARAM2 and used in action
new ScriptArgs().setParam2(100));
// Settle // Settle
settle(); settle();
...@@ -139,8 +141,7 @@ public class DungeonChallenge { ...@@ -139,8 +141,7 @@ public class DungeonChallenge {
if(!stage){ if(!stage){
getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE, getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE,
// TODO record the time in PARAM2 and used in action new ScriptArgs(this.isSuccess() ? 1 : 0));
new ScriptArgs(this.isSuccess() ? 1 : 0, 100));
} }
} }
......
...@@ -29,6 +29,7 @@ import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo; ...@@ -29,6 +29,7 @@ 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.net.proto.WorktopInfoOuterClass.WorktopInfo;
import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneGadget;
import emu.grasscutter.scripts.data.ScriptArgs; import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketGadgetStateNotify; import emu.grasscutter.server.packet.send.PacketGadgetStateNotify;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
...@@ -51,6 +52,7 @@ public class EntityGadget extends EntityBaseGadget { ...@@ -51,6 +52,7 @@ public class EntityGadget extends EntityBaseGadget {
private int state; private int state;
private int pointType; private int pointType;
private GadgetContent content; 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);
...@@ -99,6 +101,7 @@ public class EntityGadget extends EntityBaseGadget { ...@@ -99,6 +101,7 @@ public class EntityGadget extends EntityBaseGadget {
public void updateState(int state){ public void updateState(int state){
this.setState(state); this.setState(state);
this.getScene().broadcastPacket(new PacketGadgetStateNotify(this, state)); this.getScene().broadcastPacket(new PacketGadgetStateNotify(this, state));
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_STATE_CHANGE, new ScriptArgs(state, this.getConfigId()));
} }
public int getPointType() { public int getPointType() {
...@@ -118,6 +121,14 @@ public class EntityGadget extends EntityBaseGadget { ...@@ -118,6 +121,14 @@ public class EntityGadget extends EntityBaseGadget {
this.content = this.content == null ? content : this.content; this.content = this.content == null ? content : this.content;
} }
public SceneGadget getMetaGadget() {
return metaGadget;
}
public void setMetaGadget(SceneGadget metaGadget) {
this.metaGadget = metaGadget;
}
// TODO refactor // TODO refactor
public void buildContent() { public void buildContent() {
if (getContent() != null || getGadgetData() == null || getGadgetData().getType() == null) { if (getContent() != null || getGadgetData() == null || getGadgetData().getType() == null) {
......
package emu.grasscutter.game.entity.gadget; package emu.grasscutter.game.entity.gadget;
import java.util.Random;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.player.Player; 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.InteractTypeOuterClass.InteractType;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo; import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.scripts.constants.ScriptGadgetState; import emu.grasscutter.scripts.constants.ScriptGadgetState;
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp; import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
import static emu.grasscutter.net.proto.InterOpTypeOuterClass.InterOpType.INTER_OP_START;
public class GadgetChest extends GadgetContent { public class GadgetChest extends GadgetContent {
public GadgetChest(EntityGadget gadget) { public GadgetChest(EntityGadget gadget) {
super(gadget); super(gadget);
} }
public boolean onInteract(Player player) { public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
var chestRewardMap = getGadget().getScene().getWorld().getServer().getWorldDataManager().getChestRewardMap(); var chestInteractHandlerMap = getGadget().getScene().getWorld().getServer().getWorldDataManager().getChestInteractHandlerMap();
var chestReward = chestRewardMap.get(getGadget().getGadgetData().getJsonName()); var handler = chestInteractHandlerMap.get(getGadget().getGadgetData().getJsonName());
if (chestReward == null) { if(handler == null){
Grasscutter.getLogger().warn("Could not found the config of this type of Chests {}", getGadget().getGadgetData().getJsonName()); Grasscutter.getLogger().warn("Could not found the handler of this type of Chests {}", getGadget().getGadgetData().getJsonName());
return true; return false;
}
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++){
getGadget().getScene().addItemEntity(chestReward.getContent().get(i).getItemId(), chestReward.getContent().get(i).getCount(), getGadget());
} }
var random = new Random(System.currentTimeMillis()); if(opType == INTER_OP_START && handler.isTwoStep()){
for(int i=0;i<chestReward.getRandomCount();i++){ player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_OPEN_CHEST, INTER_OP_START));
var index = random.nextInt(chestReward.getRandomContent().size()); return false;
var item = chestReward.getRandomContent().get(index); }else{
getGadget().getScene().addItemEntity(item.getItemId(), item.getCount(), getGadget()); var success = handler.onInteract(this, player);
if (!success){
return false;
} }
getGadget().updateState(ScriptGadgetState.ChestOpened); getGadget().updateState(ScriptGadgetState.ChestOpened);
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_OPEN_CHEST)); player.sendPacket(new PacketGadgetInteractRsp(this.getGadget(), InteractTypeOuterClass.InteractType.INTERACT_OPEN_CHEST));
return true; return true;
} }
}
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) { 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());
}
} }
} }
...@@ -2,6 +2,7 @@ package emu.grasscutter.game.entity.gadget; ...@@ -2,6 +2,7 @@ package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo; import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
public abstract class GadgetContent { public abstract class GadgetContent {
...@@ -15,7 +16,7 @@ public abstract class GadgetContent { ...@@ -15,7 +16,7 @@ public abstract class GadgetContent {
return gadget; return gadget;
} }
public abstract boolean onInteract(Player player); public abstract boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType);
public abstract void onBuildProto(SceneGadgetInfo.Builder gadgetInfo); public abstract void onBuildProto(SceneGadgetInfo.Builder gadgetInfo);
} }
...@@ -7,6 +7,7 @@ import emu.grasscutter.game.inventory.GameItem; ...@@ -7,6 +7,7 @@ import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo; import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo;
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo; import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
public class GadgetGatherPoint extends GadgetContent { public class GadgetGatherPoint extends GadgetContent {
...@@ -25,7 +26,7 @@ public class GadgetGatherPoint extends GadgetContent { ...@@ -25,7 +26,7 @@ public class GadgetGatherPoint extends GadgetContent {
return getGatherData().getItemId(); return getGatherData().getItemId();
} }
public boolean onInteract(Player player) { public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
GameItem item = new GameItem(gatherData.getItemId(), 1); GameItem item = new GameItem(gatherData.getItemId(), 1);
player.getInventory().addItem(item, ActionReason.Gather); player.getInventory().addItem(item, ActionReason.Gather);
......
...@@ -2,6 +2,7 @@ package emu.grasscutter.game.entity.gadget; ...@@ -2,6 +2,7 @@ package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
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.SceneGadgetInfoOuterClass.SceneGadgetInfo; import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp; import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
...@@ -12,7 +13,7 @@ public class GadgetRewardStatue extends GadgetContent { ...@@ -12,7 +13,7 @@ public class GadgetRewardStatue extends GadgetContent {
super(gadget); super(gadget);
} }
public boolean onInteract(Player player) { public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
if (player.getScene().getChallenge() != null) { if (player.getScene().getChallenge() != null) {
player.getScene().getChallenge().getStatueDrops(player); player.getScene().getChallenge().getStatueDrops(player);
} }
......
...@@ -4,6 +4,7 @@ import java.util.Arrays; ...@@ -4,6 +4,7 @@ import java.util.Arrays;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo; import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.net.proto.WorktopInfoOuterClass.WorktopInfo; import emu.grasscutter.net.proto.WorktopInfoOuterClass.WorktopInfo;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
...@@ -34,7 +35,7 @@ public class GadgetWorktop extends GadgetContent { ...@@ -34,7 +35,7 @@ public class GadgetWorktop extends GadgetContent {
this.worktopOptions.remove(option); this.worktopOptions.remove(option);
} }
public boolean onInteract(Player player) { public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
return false; return false;
} }
......
package emu.grasscutter.game.entity.gadget.chest;
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);
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;
}
}
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);
}
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;
}
}
...@@ -25,11 +25,8 @@ import emu.grasscutter.game.mail.MailHandler; ...@@ -25,11 +25,8 @@ import emu.grasscutter.game.mail.MailHandler;
import emu.grasscutter.game.managers.StaminaManager.StaminaManager; import emu.grasscutter.game.managers.StaminaManager.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.GameMainQuest;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestManager; import emu.grasscutter.game.quest.QuestManager;
import emu.grasscutter.game.shop.ShopLimit; import emu.grasscutter.game.shop.ShopLimit;
import emu.grasscutter.game.managers.MapMarkManager.*; import emu.grasscutter.game.managers.MapMarkManager.*;
...@@ -47,7 +44,6 @@ import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo; ...@@ -47,7 +44,6 @@ import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo;
import emu.grasscutter.net.proto.PlayerLocationInfoOuterClass.PlayerLocationInfo; import emu.grasscutter.net.proto.PlayerLocationInfoOuterClass.PlayerLocationInfo;
import emu.grasscutter.net.proto.ProfilePictureOuterClass.ProfilePicture; import emu.grasscutter.net.proto.ProfilePictureOuterClass.ProfilePicture;
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
import emu.grasscutter.scripts.constants.ScriptGadgetState;
import emu.grasscutter.server.event.player.PlayerJoinEvent; import emu.grasscutter.server.event.player.PlayerJoinEvent;
import emu.grasscutter.server.event.player.PlayerQuitEvent; import emu.grasscutter.server.event.player.PlayerQuitEvent;
import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameServer;
...@@ -890,7 +886,7 @@ public class Player { ...@@ -890,7 +886,7 @@ public class Player {
return this.getMailHandler().replaceMailByIndex(index, message); return this.getMailHandler().replaceMailByIndex(index, message);
} }
public void interactWith(int gadgetEntityId) { public void interactWith(int gadgetEntityId, InterOpTypeOuterClass.InterOpType opType) {
GameEntity entity = getScene().getEntityById(gadgetEntityId); GameEntity entity = getScene().getEntityById(gadgetEntityId);
if (entity == null) { if (entity == null) {
...@@ -923,7 +919,7 @@ public class Player { ...@@ -923,7 +919,7 @@ public class Player {
return; return;
} }
boolean shouldDelete = gadget.getContent().onInteract(this); boolean shouldDelete = gadget.getContent().onInteract(this, opType);
if (shouldDelete) { if (shouldDelete) {
entity.getScene().removeEntity(entity); entity.getScene().removeEntity(entity);
......
...@@ -3,42 +3,60 @@ package emu.grasscutter.game.world; ...@@ -3,42 +3,60 @@ package emu.grasscutter.game.world;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.DataLoader; 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 emu.grasscutter.server.game.GameServer;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static emu.grasscutter.Configuration.DATA;
public class WorldDataManager { public class WorldDataManager {
private final GameServer gameServer; private final GameServer gameServer;
private final Map<String, ChestReward> chestRewardMap; private final Map<String, ChestInteractHandler> chestInteractHandlerMap; // chestType-Handler
public WorldDataManager(GameServer gameServer){ public WorldDataManager(GameServer gameServer){
this.gameServer = gameServer; this.gameServer = gameServer;
this.chestRewardMap = new HashMap<>(); this.chestInteractHandlerMap = new HashMap<>();
load(); loadChestConfig();
} }
public synchronized void load(){ 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)) { try(InputStream is = DataLoader.load("ChestReward.json"); InputStreamReader isr = new InputStreamReader(is)) {
List<ChestReward> chestReward = Grasscutter.getGsonFactory().fromJson( List<ChestReward> chestReward = Grasscutter.getGsonFactory().fromJson(
isr, isr,
TypeToken.getParameterized(List.class, ChestReward.class).getType()); TypeToken.getParameterized(List.class, ChestReward.class).getType());
chestReward.forEach(reward -> chestReward.forEach(reward ->
reward.getObjNames().forEach(name -> chestRewardMap.put(name, reward))); reward.getObjNames().forEach(
name -> chestInteractHandlerMap.putIfAbsent(name, new NormalChestInteractHandler(reward))));
} catch (Exception e) { } catch (Exception e) {
Grasscutter.getLogger().error("Unable to load chest reward config.", e); Grasscutter.getLogger().error("Unable to load chest reward config.", e);
} }
} }
public Map<String, ChestReward> getChestRewardMap() { public Map<String, ChestInteractHandler> getChestInteractHandlerMap() {
return chestRewardMap; 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());
} }
} }
...@@ -14,6 +14,7 @@ import emu.grasscutter.scripts.constants.EventType; ...@@ -14,6 +14,7 @@ import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.*; 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.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.luaj.vm2.LuaError; import org.luaj.vm2.LuaError;
...@@ -21,6 +22,10 @@ import org.luaj.vm2.LuaValue; ...@@ -21,6 +22,10 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.jse.CoerceJavaToLua; import org.luaj.vm2.lib.jse.CoerceJavaToLua;
import java.util.*; import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class SceneScriptManager { public class SceneScriptManager {
private final Scene scene; private final Scene scene;
...@@ -43,6 +48,12 @@ public class SceneScriptManager { ...@@ -43,6 +48,12 @@ public class SceneScriptManager {
* blockid - loaded groupSet * blockid - loaded groupSet
*/ */
private Int2ObjectMap<Set<SceneGroup>> loadedGroupSetPerBlock; private Int2ObjectMap<Set<SceneGroup>> loadedGroupSetPerBlock;
public static final ExecutorService eventExecutor;
static {
eventExecutor = new ThreadPoolExecutor(4, 4,
60, TimeUnit.SECONDS, new LinkedBlockingDeque<>(100),
FastThreadLocalThread::new, new ThreadPoolExecutor.AbortPolicy());
}
public SceneScriptManager(Scene scene) { public SceneScriptManager(Scene scene) {
this.scene = scene; this.scene = scene;
this.triggers = new HashMap<>(); this.triggers = new HashMap<>();
...@@ -211,7 +222,7 @@ public class SceneScriptManager { ...@@ -211,7 +222,7 @@ public class SceneScriptManager {
} }
var toCreate = gadgets.stream() var toCreate = gadgets.stream()
.map(g -> createGadget(g.groupId, group.block_id, g)) .map(g -> createGadget(g.group.id, group.block_id, g))
.filter(Objects::nonNull) .filter(Objects::nonNull)
.toList(); .toList();
this.addEntities(toCreate); this.addEntities(toCreate);
...@@ -254,8 +265,17 @@ public class SceneScriptManager { ...@@ -254,8 +265,17 @@ public class SceneScriptManager {
getScene().addEntity(createMonster(group.id, group.block_id, group.monsters.get(configId))); getScene().addEntity(createMonster(group.id, group.block_id, group.monsters.get(configId)));
} }
// Events // Events
public void callEvent(int eventType, ScriptArgs params){ 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));
}
private void realCallEvent(int eventType, ScriptArgs params) {
try{ try{
ScriptLoader.getScriptLib().setSceneScriptManager(this); ScriptLoader.getScriptLib().setSceneScriptManager(this);
for (SceneTrigger trigger : this.getTriggersByEvent(eventType)) { for (SceneTrigger trigger : this.getTriggersByEvent(eventType)) {
...@@ -282,7 +302,7 @@ public class SceneScriptManager { ...@@ -282,7 +302,7 @@ public class SceneScriptManager {
} }
} }
public LuaValue callScriptFunc(String funcName, SceneGroup group, ScriptArgs params){ private LuaValue callScriptFunc(String funcName, SceneGroup group, ScriptArgs params){
LuaValue funcLua = null; LuaValue funcLua = null;
if (funcName != null && !funcName.isEmpty()) { if (funcName != null && !funcName.isEmpty()) {
funcLua = (LuaValue) group.getBindings().get(funcName); funcLua = (LuaValue) group.getBindings().get(funcName);
...@@ -332,11 +352,9 @@ public class SceneScriptManager { ...@@ -332,11 +352,9 @@ public class SceneScriptManager {
entity.getRotation().set(g.rot); entity.getRotation().set(g.rot);
entity.setState(g.state); entity.setState(g.state);
entity.setPointType(g.point_type); entity.setPointType(g.point_type);
entity.setMetaGadget(g);
entity.buildContent(); entity.buildContent();
// Lua event
this.callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(entity.getConfigId()));
return entity; return entity;
} }
......
...@@ -8,7 +8,6 @@ import emu.grasscutter.game.entity.gadget.GadgetWorktop; ...@@ -8,7 +8,6 @@ import emu.grasscutter.game.entity.gadget.GadgetWorktop;
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 io.netty.util.concurrent.FastThreadLocal;
import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaTable;
...@@ -16,7 +15,6 @@ import org.luaj.vm2.LuaValue; ...@@ -16,7 +15,6 @@ 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 {
...@@ -69,33 +67,23 @@ public class ScriptLib { ...@@ -69,33 +67,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) {
if (!(entity instanceof EntityGadget)) {
continue;
}
EntityGadget gadget = (EntityGadget) entity;
gadget.setState(gadgetState);
getSceneScriptManager().getScene().broadcastPacket(new PacketGadgetStateNotify(gadget, gadgetState)); getSceneScriptManager().getScene().getEntities().values().stream()
} .filter(e -> e.getGroupId() == groupId)
.filter(e -> e instanceof EntityGadget)
.map(e -> (EntityGadget)e)
.forEach(e -> e.updateState(gadgetState));
return 0; return 0;
} }
...@@ -450,8 +438,9 @@ public class ScriptLib { ...@@ -450,8 +438,9 @@ public class ScriptLib {
if (entity instanceof EntityGadget entityGadget) { if (entity instanceof EntityGadget entityGadget) {
entityGadget.updateState(state); entityGadget.updateState(state);
return 0;
} }
return 0; return 1;
} }
} }
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;
}
...@@ -9,4 +9,5 @@ public class SceneGadget extends SceneObject{ ...@@ -9,4 +9,5 @@ public class SceneGadget extends SceneObject{
public int gadget_id; public int gadget_id;
public int state; public int state;
public int point_type; public int point_type;
public SceneBossChest boss_chest;
} }
...@@ -98,11 +98,11 @@ public class SceneGroup { ...@@ -98,11 +98,11 @@ public class SceneGroup {
// Set // Set
monsters = ScriptLoader.getSerializer().toList(SceneMonster.class, bindings.get("monsters")).stream() monsters = ScriptLoader.getSerializer().toList(SceneMonster.class, bindings.get("monsters")).stream()
.collect(Collectors.toMap(x -> x.config_id, y -> y)); .collect(Collectors.toMap(x -> x.config_id, y -> y));
monsters.values().forEach(m -> m.groupId = id); monsters.values().forEach(m -> m.group = this);
gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, bindings.get("gadgets")).stream() gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, bindings.get("gadgets")).stream()
.collect(Collectors.toMap(x -> x.config_id, y -> y)); .collect(Collectors.toMap(x -> x.config_id, y -> y));
gadgets.values().forEach(m -> m.groupId = id); gadgets.values().forEach(m -> m.group = this);
triggers = ScriptLoader.getSerializer().toList(SceneTrigger.class, bindings.get("triggers")).stream() triggers = ScriptLoader.getSerializer().toList(SceneTrigger.class, bindings.get("triggers")).stream()
.collect(Collectors.toMap(x -> x.name, y -> y)); .collect(Collectors.toMap(x -> x.name, y -> y));
......
...@@ -16,5 +16,5 @@ public class SceneObject { ...@@ -16,5 +16,5 @@ public class SceneObject {
/** /**
* not set by lua * not set by lua
*/ */
public transient int groupId; public transient SceneGroup group;
} }
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment