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<TowerLevelData> towerLevelDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<TowerScheduleData> towerScheduleDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<InvestigationMonsterData> investigationMonsterDataMap = new Int2ObjectOpenHashMap<>();
// Cache
private static Map<Integer, List<Integer>> fetters = new HashMap<>();
@ -352,4 +353,8 @@ public class GameData {
public static Int2ObjectMap<GatherData> getGatherDataMap() {
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()) {
// 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();
@ -139,8 +141,7 @@ public class DungeonChallenge {
if(!stage){
getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE,
// TODO record the time in PARAM2 and used in action
new ScriptArgs(this.isSuccess() ? 1 : 0, 100));
new ScriptArgs(this.isSuccess() ? 1 : 0));
}
}

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.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.utils.Position;
@ -51,6 +52,7 @@ public class EntityGadget extends EntityBaseGadget {
private int state;
private int pointType;
private GadgetContent content;
private SceneGadget metaGadget;
public EntityGadget(Scene scene, int gadgetId, Position pos) {
super(scene);
@ -99,6 +101,7 @@ public class EntityGadget extends EntityBaseGadget {
public void updateState(int state){
this.setState(state);
this.getScene().broadcastPacket(new PacketGadgetStateNotify(this, state));
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_STATE_CHANGE, new ScriptArgs(state, this.getConfigId()));
}
public int getPointType() {
@ -117,7 +120,15 @@ public class EntityGadget extends EntityBaseGadget {
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) {

View File

@ -1,53 +1,63 @@
package emu.grasscutter.game.entity.gadget;
import java.util.Random;
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) {
var chestRewardMap = getGadget().getScene().getWorld().getServer().getWorldDataManager().getChestRewardMap();
var chestReward = chestRewardMap.get(getGadget().getGadgetData().getJsonName());
if (chestReward == null) {
Grasscutter.getLogger().warn("Could not found the config of this type of Chests {}", getGadget().getGadgetData().getJsonName());
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));
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) {
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.player.Player;
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
public abstract class GadgetContent {
@ -15,7 +16,7 @@ public abstract class GadgetContent {
return gadget;
}
public abstract boolean onInteract(Player player);
public abstract boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType);
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.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 {
@ -25,7 +26,7 @@ public class GadgetGatherPoint extends GadgetContent {
return getGatherData().getItemId();
}
public boolean onInteract(Player player) {
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
GameItem item = new GameItem(gatherData.getItemId(), 1);
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.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;
@ -12,7 +13,7 @@ public class GadgetRewardStatue extends GadgetContent {
super(gadget);
}
public boolean onInteract(Player player) {
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
if (player.getScene().getChallenge() != null) {
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.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;
@ -34,7 +35,7 @@ public class GadgetWorktop extends GadgetContent {
this.worktopOptions.remove(option);
}
public boolean onInteract(Player player) {
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
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.SotSManager;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.game.props.PlayerProperty;
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.shop.ShopLimit;
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.ProfilePictureOuterClass.ProfilePicture;
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.PlayerQuitEvent;
import emu.grasscutter.server.game.GameServer;
@ -890,7 +886,7 @@ public class Player {
return this.getMailHandler().replaceMailByIndex(index, message);
}
public void interactWith(int gadgetEntityId) {
public void interactWith(int gadgetEntityId, InterOpTypeOuterClass.InterOpType opType) {
GameEntity entity = getScene().getEntityById(gadgetEntityId);
if (entity == null) {
@ -923,7 +919,7 @@ public class Player {
return;
}
boolean shouldDelete = gadget.getContent().onInteract(this);
boolean shouldDelete = gadget.getContent().onInteract(this, opType);
if (shouldDelete) {
entity.getScene().removeEntity(entity);

View File

@ -3,42 +3,60 @@ 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.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static emu.grasscutter.Configuration.DATA;
public class WorldDataManager {
private final GameServer gameServer;
private final Map<String, ChestReward> chestRewardMap;
private final Map<String, ChestInteractHandler> chestInteractHandlerMap; // chestType-Handler
public WorldDataManager(GameServer gameServer){
this.gameServer = gameServer;
this.chestRewardMap = new HashMap<>();
load();
this.chestInteractHandlerMap = new HashMap<>();
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)) {
List<ChestReward> chestReward = Grasscutter.getGsonFactory().fromJson(
isr,
TypeToken.getParameterized(List.class, ChestReward.class).getType());
chestReward.forEach(reward ->
reward.getObjNames().forEach(name -> chestRewardMap.put(name, 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, ChestReward> getChestRewardMap() {
return chestRewardMap;
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());
}
}

View File

@ -14,6 +14,7 @@ import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.*;
import emu.grasscutter.scripts.service.ScriptMonsterSpawnService;
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;
@ -21,6 +22,10 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
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 {
private final Scene scene;
@ -43,6 +48,12 @@ public class SceneScriptManager {
* blockid - loaded groupSet
*/
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) {
this.scene = scene;
this.triggers = new HashMap<>();
@ -211,7 +222,7 @@ public class SceneScriptManager {
}
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)
.toList();
this.addEntities(toCreate);
@ -254,8 +265,17 @@ public class SceneScriptManager {
getScene().addEntity(createMonster(group.id, group.block_id, group.monsters.get(configId)));
}
// 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{
ScriptLoader.getScriptLib().setSceneScriptManager(this);
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;
if (funcName != null && !funcName.isEmpty()) {
funcLua = (LuaValue) group.getBindings().get(funcName);
@ -332,10 +352,8 @@ public class SceneScriptManager {
entity.getRotation().set(g.rot);
entity.setState(g.state);
entity.setPointType(g.point_type);
entity.setMetaGadget(g);
entity.buildContent();
// Lua event
this.callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(entity.getConfigId()));
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.SceneRegion;
import emu.grasscutter.server.packet.send.PacketCanUseSkillNotify;
import emu.grasscutter.server.packet.send.PacketGadgetStateNotify;
import emu.grasscutter.server.packet.send.PacketWorktopOptionNotify;
import io.netty.util.concurrent.FastThreadLocal;
import org.luaj.vm2.LuaTable;
@ -16,7 +15,6 @@ import org.luaj.vm2.LuaValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Optional;
public class ScriptLib {
@ -68,34 +66,24 @@ public class ScriptLib {
if (entity.isEmpty()) {
return 1;
}
if (!(entity.get() instanceof EntityGadget)) {
return 1;
if (entity.get() instanceof EntityGadget entityGadget) {
entityGadget.updateState(gadgetState);
return 0;
}
EntityGadget gadget = (EntityGadget) entity.get();
gadget.setState(gadgetState);
getSceneScriptManager().getScene().broadcastPacket(new PacketGadgetStateNotify(gadget, gadgetState));
return 0;
return 1;
}
public int SetGroupGadgetStateByConfigId(int groupId, int configId, int gadgetState) {
logger.debug("[LUA] Call SetGroupGadgetStateByConfigId with {},{},{}",
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;
}
@ -450,8 +438,9 @@ public class ScriptLib {
if (entity instanceof EntityGadget entityGadget) {
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 state;
public int point_type;
public SceneBossChest boss_chest;
}

View File

@ -98,11 +98,11 @@ public class SceneGroup {
// 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.groupId = id);
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.groupId = id);
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));

View File

@ -16,5 +16,5 @@ public class SceneObject {
/**
* 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 {
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.PacketOpcodes;
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.RetcodeOuterClass;
public class PacketGadgetInteractRsp extends BasePacket {
public PacketGadgetInteractRsp(EntityBaseGadget gadget, InteractType interact) {
this(gadget, interact, null);
}
public PacketGadgetInteractRsp(EntityBaseGadget gadget, InteractType interact, InterOpTypeOuterClass.InterOpType opType) {
super(PacketOpcodes.GadgetInteractRsp);
GadgetInteractRsp proto = GadgetInteractRsp.newBuilder()
var proto = GadgetInteractRsp.newBuilder()
.setGadgetEntityId(gadget.getId())
.setInteractType(interact)
.setGadgetId(gadget.getGadgetId())
.build();
.setGadgetId(gadget.getGadgetId());
if(opType != null){
proto.setOpType(opType);
}
this.setData(proto);
this.setData(proto.build());
}
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());
}
}