Implement local specialty spawning

This commit is contained in:
Melledy 2022-05-18 02:21:34 -07:00
parent df580bf519
commit a48585d124
18 changed files with 328 additions and 67 deletions

View File

@ -75,6 +75,7 @@ public class GameData {
private static final Int2ObjectMap<ShopGoodsData> shopGoodsDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<ShopGoodsData> shopGoodsDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<CombineData> combineDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<CombineData> combineDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<RewardPreviewData> rewardPreviewDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<RewardPreviewData> rewardPreviewDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<GatherData> gatherDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<TowerFloorData> towerFloorDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<TowerFloorData> towerFloorDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<TowerLevelData> towerLevelDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<TowerLevelData> towerLevelDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<TowerScheduleData> towerScheduleDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<TowerScheduleData> towerScheduleDataMap = new Int2ObjectOpenHashMap<>();
@ -347,4 +348,8 @@ public class GameData {
public static Int2ObjectMap<QuestData> getQuestDataMap() { public static Int2ObjectMap<QuestData> getQuestDataMap() {
return questDataMap; return questDataMap;
} }
public static Int2ObjectMap<GatherData> getGatherDataMap() {
return gatherDataMap;
}
} }

View File

@ -0,0 +1,49 @@
package emu.grasscutter.data.def;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "GatherExcelConfigData.json")
public class GatherData extends GameResource {
private int PointType;
private int Id;
private int GadgetId;
private int ItemId;
private int Cd; // Probably hours
private boolean IsForbidGuest;
private boolean InitDisableInteract;
@Override
public int getId() {
return this.PointType;
}
public int getGatherId() {
return Id;
}
public int getGadgetId() {
return GadgetId;
}
public int getItemId() {
return ItemId;
}
public int getCd() {
return Cd;
}
public boolean isForbidGuest() {
return IsForbidGuest;
}
public boolean initDisableInteract() {
return InitDisableInteract;
}
@Override
public void onLoad() {
}
}

View File

@ -12,7 +12,6 @@ public class SceneData extends GameResource {
private SceneType Type; private SceneType Type;
private String ScriptData; private String ScriptData;
@Override @Override
public int getId() { public int getId() {
return this.Id; return this.Id;

View File

@ -5,8 +5,13 @@ import java.util.List;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.GadgetData; import emu.grasscutter.data.def.GadgetData;
import emu.grasscutter.game.entity.gadget.GadgetContent;
import emu.grasscutter.game.entity.gadget.GadgetGatherPoint;
import emu.grasscutter.game.entity.gadget.GadgetRewardStatue;
import emu.grasscutter.game.entity.gadget.GadgetWorktop;
import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.EntityType; import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.world.World; import emu.grasscutter.game.world.World;
@ -39,7 +44,8 @@ public class EntityGadget extends EntityBaseGadget {
private int gadgetId; private int gadgetId;
private int state; private int state;
private IntSet worktopOptions; private int pointType;
private GadgetContent content;
public EntityGadget(Scene scene, int gadgetId, Position pos) { public EntityGadget(Scene scene, int gadgetId, Position pos) {
super(scene); super(scene);
@ -50,19 +56,22 @@ public class EntityGadget extends EntityBaseGadget {
this.rot = new Position(); this.rot = new Position();
} }
public EntityGadget(Scene scene, int gadgetId, Position pos, GadgetContent content) {
this(scene, gadgetId, pos);
this.content = content;
}
public GadgetData getGadgetData() { public GadgetData getGadgetData() {
return data; return data;
} }
@Override @Override
public Position getPosition() { public Position getPosition() {
// TODO Auto-generated method stub
return this.pos; return this.pos;
} }
@Override @Override
public Position getRotation() { public Position getRotation() {
// TODO Auto-generated method stub
return this.rot; return this.rot;
} }
@ -82,27 +91,42 @@ public class EntityGadget extends EntityBaseGadget {
this.state = state; this.state = state;
} }
public IntSet getWorktopOptions() { public int getPointType() {
return worktopOptions; return pointType;
} }
public void addWorktopOptions(int[] options) { public void setPointType(int pointType) {
if (this.worktopOptions == null) { this.pointType = pointType;
this.worktopOptions = new IntOpenHashSet();
}
Arrays.stream(options).forEach(this.worktopOptions::add);
} }
public void removeWorktopOption(int option) { public GadgetContent getContent() {
if (this.worktopOptions == null) { return content;
}
@Deprecated // Dont use!
public void setContent(GadgetContent content) {
this.content = this.content == null ? content : this.content;
}
// TODO refactor
public void buildContent() {
if (getContent() != null || getGadgetData() == null || getGadgetData().getType() == null) {
return; return;
} }
this.worktopOptions.remove(option);
EntityType type = getGadgetData().getType();
GadgetContent content = switch (type) {
case GatherPoint -> new GadgetGatherPoint(this);
case Worktop -> new GadgetWorktop(this);
case RewardStatue -> new GadgetRewardStatue(this);
default -> null;
};
this.content = content;
} }
@Override @Override
public Int2FloatOpenHashMap getFightProperties() { public Int2FloatOpenHashMap getFightProperties() {
// TODO Auto-generated method stub
return null; return null;
} }
@ -143,11 +167,8 @@ public class EntityGadget extends EntityBaseGadget {
.setIsEnableInteract(true) .setIsEnableInteract(true)
.setAuthorityPeerId(this.getScene().getWorld().getHostPeerId()); .setAuthorityPeerId(this.getScene().getWorld().getHostPeerId());
if (this.getGadgetData().getType() == EntityType.Worktop && this.getWorktopOptions() != null) { if (this.getContent() != null) {
WorktopInfo worktop = WorktopInfo.newBuilder() this.getContent().onBuildProto(gadgetInfo);
.addAllOptionList(this.getWorktopOptions())
.build();
gadgetInfo.setWorktop(worktop);
} }
entityInfo.setGadget(gadgetInfo); entityInfo.setGadget(gadgetInfo);

View File

@ -0,0 +1,21 @@
package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
public abstract class GadgetContent {
private final EntityGadget gadget;
public GadgetContent(EntityGadget gadget) {
this.gadget = gadget;
}
public EntityGadget getGadget() {
return gadget;
}
public abstract boolean onInteract(Player player);
public abstract void onBuildProto(SceneGadgetInfo.Builder gadgetInfo);
}

View File

@ -0,0 +1,44 @@
package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.GatherData;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
public class GadgetGatherPoint extends GadgetContent {
private GatherData gatherData;
public GadgetGatherPoint(EntityGadget gadget) {
super(gadget);
this.gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
}
public GatherData getGatherData() {
return gatherData;
}
public int getItemId() {
return getGatherData().getItemId();
}
public boolean onInteract(Player player) {
GameItem item = new GameItem(gatherData.getItemId(), 1);
player.getInventory().addItem(item, ActionReason.Gather);
return true;
}
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
GatherGadgetInfo gatherGadgetInfo = GatherGadgetInfo.newBuilder()
.setItemId(this.getItemId())
.setIsForbidGuest(this.getGatherData().isForbidGuest())
.build();
gadgetInfo.setGatherGadget(gatherGadgetInfo);
}
}

View File

@ -0,0 +1,28 @@
package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
public class GadgetRewardStatue extends GadgetContent {
public GadgetRewardStatue(EntityGadget gadget) {
super(gadget);
}
public boolean onInteract(Player player) {
if (player.getScene().getChallenge() != null) {
player.getScene().getChallenge().getStatueDrops(player);
}
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_OPEN_STATUE));
return false;
}
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
}
}

View File

@ -0,0 +1,52 @@
package emu.grasscutter.game.entity.gadget;
import java.util.Arrays;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.net.proto.WorktopInfoOuterClass.WorktopInfo;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
public class GadgetWorktop extends GadgetContent {
private IntSet worktopOptions;
public GadgetWorktop(EntityGadget gadget) {
super(gadget);
}
public IntSet getWorktopOptions() {
return worktopOptions;
}
public void addWorktopOptions(int[] options) {
if (this.worktopOptions == null) {
this.worktopOptions = new IntOpenHashSet();
}
Arrays.stream(options).forEach(this.worktopOptions::add);
}
public void removeWorktopOption(int option) {
if (this.worktopOptions == null) {
return;
}
this.worktopOptions.remove(option);
}
public boolean onInteract(Player player) {
return false;
}
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
if (this.worktopOptions == null) {
return;
}
WorktopInfo worktop = WorktopInfo.newBuilder()
.addAllOptionList(this.getWorktopOptions())
.build();
gadgetInfo.setWorktop(worktop);
}
}

View File

@ -917,22 +917,20 @@ public class Player {
else else
this.getScene().broadcastPacket(new PacketGadgetInteractRsp(drop, InteractType.INTERACT_PICK_ITEM)); this.getScene().broadcastPacket(new PacketGadgetInteractRsp(drop, InteractType.INTERACT_PICK_ITEM));
} }
} else if (entity instanceof EntityGadget) { } else if (entity instanceof EntityGadget gadget) {
EntityGadget gadget = (EntityGadget) entity; if (gadget.getContent() == null) {
return;
}
if (gadget.getGadgetData().getType() == EntityType.RewardStatue) { boolean shouldDelete = gadget.getContent().onInteract(this);
if (scene.getChallenge() != null) {
scene.getChallenge().getStatueDrops(this);
}
this.sendPacket(new PacketGadgetInteractRsp(gadget, InteractType.INTERACT_OPEN_STATUE)); if (shouldDelete) {
entity.getScene().removeEntity(entity);
} }
} else { } else {
// Delete directly // Delete directly
entity.getScene().removeEntity(entity); entity.getScene().removeEntity(entity);
} }
return;
} }
public void onPause() { public void onPause() {

View File

@ -23,6 +23,7 @@ import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
import emu.grasscutter.scripts.SceneIndexManager; import emu.grasscutter.scripts.SceneIndexManager;
import emu.grasscutter.scripts.SceneScriptManager; import emu.grasscutter.scripts.SceneScriptManager;
import emu.grasscutter.scripts.data.SceneBlock; import emu.grasscutter.scripts.data.SceneBlock;
import emu.grasscutter.scripts.data.SceneGadget;
import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.server.packet.send.PacketAvatarSkillInfoNotify; import emu.grasscutter.server.packet.send.PacketAvatarSkillInfoNotify;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify; import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
@ -578,6 +579,14 @@ public class Scene {
continue; continue;
} }
// Load garbages
List<SceneGadget> garbageGadgets = group.getGarbageGadgets();
if (garbageGadgets != null) {
garbageGadgets.forEach(g -> scriptManager.createGadgets(group.id, group.block_id, g));
}
// Load suites
int suite = group.init_config.suite; int suite = group.init_config.suite;
if (suite == 0) { if (suite == 0) {

View File

@ -362,6 +362,8 @@ public class SceneScriptManager {
entity.setGroupId(groupId); entity.setGroupId(groupId);
entity.getRotation().set(g.rot); entity.getRotation().set(g.rot);
entity.setState(g.state); entity.setState(g.state);
entity.setPointType(g.point_type);
entity.buildContent();
return entity; return entity;
} }

View File

@ -4,6 +4,7 @@ import emu.grasscutter.game.dungeons.DungeonChallenge;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.entity.gadget.GadgetWorktop;
import emu.grasscutter.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;
@ -83,21 +84,22 @@ public class ScriptLib {
public int SetWorktopOptionsByGroupId(int groupId, int configId, int[] options) { public int SetWorktopOptionsByGroupId(int groupId, int configId, int[] options) {
logger.debug("[LUA] Call SetWorktopOptionsByGroupId with {},{},{}", logger.debug("[LUA] Call SetWorktopOptionsByGroupId with {},{},{}",
groupId,configId,options); groupId,configId,options);
Optional<GameEntity> entity = getSceneScriptManager().getScene().getEntities().values().stream() Optional<GameEntity> entity = getSceneScriptManager().getScene().getEntities().values().stream()
.filter(e -> e.getConfigId() == configId && e.getGroupId() == groupId).findFirst(); .filter(e -> e.getConfigId() == configId && e.getGroupId() == groupId).findFirst();
if (entity.isEmpty()) {
if (entity.isEmpty() || !(entity.get() instanceof EntityGadget gadget)) {
return 1; return 1;
} }
if (!(entity.get() instanceof EntityGadget)) { if (!(gadget.getContent() instanceof GadgetWorktop worktop)) {
return 1; return 1;
} }
EntityGadget gadget = (EntityGadget) entity.get(); worktop.addWorktopOptions(options);
gadget.addWorktopOptions(options);
getSceneScriptManager().getScene().broadcastPacket(new PacketWorktopOptionNotify(gadget)); getSceneScriptManager().getScene().broadcastPacket(new PacketWorktopOptionNotify(gadget));
return 0; return 0;
} }
@ -107,18 +109,17 @@ public class ScriptLib {
Optional<GameEntity> entity = getSceneScriptManager().getScene().getEntities().values().stream() Optional<GameEntity> entity = getSceneScriptManager().getScene().getEntities().values().stream()
.filter(e -> e.getConfigId() == configId && e.getGroupId() == groupId).findFirst(); .filter(e -> e.getConfigId() == configId && e.getGroupId() == groupId).findFirst();
if (entity.isEmpty()) { if (entity.isEmpty() || !(entity.get() instanceof EntityGadget gadget)) {
return 1; return 1;
} }
if (!(entity.get() instanceof EntityGadget)) { if (!(gadget.getContent() instanceof GadgetWorktop worktop)) {
return 1; return 1;
} }
EntityGadget gadget = (EntityGadget) entity.get(); worktop.removeWorktopOption(option);
gadget.removeWorktopOption(option);
getSceneScriptManager().getScene().broadcastPacket(new PacketWorktopOptionNotify(gadget)); getSceneScriptManager().getScene().broadcastPacket(new PacketWorktopOptionNotify(gadget));
return 0; return 0;
} }

View File

@ -95,7 +95,7 @@ public class ScriptLoader {
return sc.get(); return sc.get();
} }
Grasscutter.getLogger().info("Loaded Script" + path); Grasscutter.getLogger().info("Loading script " + path);
File file = new File(path); File file = new File(path);
@ -106,7 +106,7 @@ public class ScriptLoader {
scriptsCache.put(path, new SoftReference<>(script)); scriptsCache.put(path, new SoftReference<>(script));
return script; return script;
} catch (Exception e) { } catch (Exception e) {
Grasscutter.getLogger().error("Loaded Script {} failed!", path, e); Grasscutter.getLogger().error("Loading script {} failed!", path, e);
return null; return null;
} }

View File

@ -0,0 +1,10 @@
package emu.grasscutter.scripts.data;
import lombok.Setter;
import lombok.ToString;
@ToString
@Setter
public class SceneBusiness {
public int type;
}

View File

@ -8,4 +8,5 @@ import lombok.ToString;
public class SceneGadget extends SceneObject{ public class SceneGadget extends SceneObject{
public int gadget_id; public int gadget_id;
public int state; public int state;
public int point_type;
} }

View File

@ -0,0 +1,12 @@
package emu.grasscutter.scripts.data;
import java.util.List;
import lombok.Setter;
import lombok.ToString;
@ToString
@Setter
public class SceneGarbage {
public List<SceneGadget> gadgets;
}

View File

@ -26,21 +26,20 @@ public class SceneGroup {
public int refresh_id; public int refresh_id;
public Position pos; public Position pos;
/** public Map<Integer,SceneMonster> monsters; // <ConfigId, Monster>
* ConfigId - Monster public Map<Integer, SceneGadget> gadgets; // <ConfigId, Gadgets>
*/
public Map<Integer,SceneMonster> monsters;
/**
* ConfigId - Gadget
*/
public Map<Integer, SceneGadget> gadgets;
public List<SceneTrigger> triggers; public List<SceneTrigger> triggers;
public List<SceneRegion> regions; public List<SceneRegion> regions;
public List<SceneSuite> suites; public List<SceneSuite> suites;
public List<SceneVar> variables;
public SceneBusiness business;
public SceneGarbage garbages;
public SceneInitConfig init_config; public SceneInitConfig init_config;
public List<SceneVar> variables;
private transient boolean loaded; // Not an actual variable in the scripts either private transient boolean loaded; // Not an actual variable in the scripts either
private transient CompiledScript script;
public boolean isLoaded() { public boolean isLoaded() {
return loaded; return loaded;
@ -50,7 +49,13 @@ public class SceneGroup {
this.loaded = loaded; this.loaded = loaded;
} }
private transient CompiledScript script; // Not an actual variable in the scripts either public int getBusinessType() {
return this.business == null ? 0 : this.business.type;
}
public List<SceneGadget> getGarbageGadgets() {
return this.garbages == null ? null : this.garbages.gadgets;
}
public CompiledScript getScript() { public CompiledScript getScript() {
return script; return script;
@ -75,6 +80,7 @@ public class SceneGroup {
} }
this.script = cs; this.script = cs;
// Eval script // Eval script
try { try {
cs.eval(bindings); cs.eval(bindings);
@ -93,12 +99,13 @@ public class SceneGroup {
suites = ScriptLoader.getSerializer().toList(SceneSuite.class, bindings.get("suites")); suites = ScriptLoader.getSerializer().toList(SceneSuite.class, bindings.get("suites"));
regions = ScriptLoader.getSerializer().toList(SceneRegion.class, bindings.get("regions")); regions = ScriptLoader.getSerializer().toList(SceneRegion.class, bindings.get("regions"));
garbages = ScriptLoader.getSerializer().toObject(SceneGarbage.class, bindings.get("garbages"));
init_config = ScriptLoader.getSerializer().toObject(SceneInitConfig.class, bindings.get("init_config")); init_config = ScriptLoader.getSerializer().toObject(SceneInitConfig.class, bindings.get("init_config"));
// Add variables to suite // Add variables to suite
variables = ScriptLoader.getSerializer().toList(SceneVar.class, bindings.get("variables")); variables = ScriptLoader.getSerializer().toList(SceneVar.class, bindings.get("variables"));
// Add monsters to suite // Add monsters and gadgets to suite
for (SceneSuite suite : suites) { for (SceneSuite suite : suites) {
suite.sceneMonsters = new ArrayList<>( suite.sceneMonsters = new ArrayList<>(
suite.monsters.stream() suite.monsters.stream()
@ -118,6 +125,7 @@ public class SceneGroup {
} catch (ScriptException e) { } catch (ScriptException e) {
Grasscutter.getLogger().error("Error loading group " + id + " in scene " + sceneId, e); Grasscutter.getLogger().error("Error loading group " + id + " in scene " + sceneId, e);
} }
Grasscutter.getLogger().info("group {} in scene {} is loaded successfully.", id, sceneId); Grasscutter.getLogger().info("group {} in scene {} is loaded successfully.", id, sceneId);
return this; return this;
} }

View File

@ -1,6 +1,7 @@
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.gadget.GadgetWorktop;
import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.WorktopOptionNotifyOuterClass.WorktopOptionNotify; import emu.grasscutter.net.proto.WorktopOptionNotifyOuterClass.WorktopOptionNotify;
@ -13,8 +14,8 @@ public class PacketWorktopOptionNotify extends BasePacket {
WorktopOptionNotify.Builder proto = WorktopOptionNotify.newBuilder() WorktopOptionNotify.Builder proto = WorktopOptionNotify.newBuilder()
.setGadgetEntityId(gadget.getId()); .setGadgetEntityId(gadget.getId());
if (gadget.getWorktopOptions() != null) { if (gadget.getContent() instanceof GadgetWorktop worktop) {
proto.addAllOptionList(gadget.getWorktopOptions()); proto.addAllOptionList(worktop.getWorktopOptions());
} }
this.setData(proto); this.setData(proto);