Support Boss Chest

This commit is contained in:
Akka 2022-05-20 13:46:00 +08:00 committed by Melledy
parent 5429469852
commit 717c2d1dd7
24 changed files with 324 additions and 94 deletions

View File

@ -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 {
public static Int2ObjectMap<GatherData> getGatherDataMap() { public static Int2ObjectMap<GatherData> getGatherDataMap() {
return gatherDataMap; return gatherDataMap;
} }
public static Int2ObjectMap<InvestigationMonsterData> getInvestigationMonsterDataMap() {
return investigationMonsterDataMap;
}
} }

View File

@ -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();
}
}

View File

@ -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 {
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));
} }
} }

View File

@ -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 {
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 {
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() {
@ -117,7 +120,15 @@ public class EntityGadget extends EntityBaseGadget {
public void setContent(GadgetContent content) { public void setContent(GadgetContent content) {
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) {

View File

@ -1,53 +1,63 @@
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 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));
return true; return true;
} }
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());
for(int i=0;i<chestReward.getRandomCount();i++){
var index = random.nextInt(chestReward.getRandomContent().size());
var item = chestReward.getRandomContent().get(index);
getGadget().getScene().addItemEntity(item.getItemId(), item.getCount(), getGadget());
}
getGadget().updateState(ScriptGadgetState.ChestOpened);
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_OPEN_CHEST));
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());
}
} }
} }

View File

@ -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 {
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);
} }

View File

@ -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 {
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);

View File

@ -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 {
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);
} }

View File

@ -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 {
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;
} }

View File

@ -0,0 +1,35 @@
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;
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
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 {
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 {
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);

View File

@ -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());
} }
} }

View File

@ -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;
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 {
* 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 {
} }
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 {
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 {
} }
} }
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,10 +352,8 @@ 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;
} }

View File

@ -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;
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 {
@ -68,34 +66,24 @@ public class ScriptLib {
if (entity.isEmpty()) { if (entity.isEmpty()) {
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(); getSceneScriptManager().getScene().getEntities().values().stream()
.filter(e -> e.getGroupId() == groupId)
for (GameEntity entity : list) { .filter(e -> e instanceof EntityGadget)
if (!(entity instanceof EntityGadget)) { .map(e -> (EntityGadget)e)
continue; .forEach(e -> e.updateState(gadgetState));
}
EntityGadget gadget = (EntityGadget) entity;
gadget.setState(gadgetState);
getSceneScriptManager().getScene().broadcastPacket(new PacketGadgetStateNotify(gadget, gadgetState));
}
return 0; return 0;
} }
@ -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;
} }
} }

View File

@ -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;
}

View File

@ -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;
} }

View File

@ -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));

View File

@ -16,5 +16,5 @@ public class SceneObject {
/** /**
* not set by lua * not set by lua
*/ */
public transient int groupId; public transient SceneGroup group;
} }

View File

@ -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()); session.getPlayer().interactWith(req.getGadgetEntityId(), req.getOpType());
} }
} }

View File

@ -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()));
}
}

View File

@ -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();
if(opType != null){
proto.setOpType(opType);
}
this.setData(proto); this.setData(proto.build());
} }
public PacketGadgetInteractRsp() { public PacketGadgetInteractRsp() {

View File

@ -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());
}
}