Commit 30c7bb94 authored by Melledy's avatar Melledy
Browse files

Merge branch 'dev-world-scripts' of https://github.com/Grasscutters/Grasscutter into development

parents 8e6aa50c 5a3e9bc3
package emu.grasscutter.scripts;
import java.util.HashMap;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import emu.grasscutter.Grasscutter;
public class ScriptUtils {
public static HashMap<Object, Object> toMap(LuaTable table) {
HashMap<Object, Object> map = new HashMap<>();
LuaValue[] rootKeys = table.keys();
for (LuaValue k : rootKeys) {
if (table.get(k).istable()) {
map.put(k, toMap(table.get(k).checktable()));
} else {
map.put(k, table.get(k));
}
}
return map;
}
public static void print(LuaTable table) {
Grasscutter.getLogger().info(toMap(table).toString());
}
}
...@@ -2,6 +2,9 @@ package emu.grasscutter.scripts.constants; ...@@ -2,6 +2,9 @@ package emu.grasscutter.scripts.constants;
public class EventType { public class EventType {
public static final int EVENT_NONE = 0; public static final int EVENT_NONE = 0;
/**
* param1: monster.configId
*/
public static final int EVENT_ANY_MONSTER_DIE = 1; public static final int EVENT_ANY_MONSTER_DIE = 1;
public static final int EVENT_ANY_GADGET_DIE = 2; public static final int EVENT_ANY_GADGET_DIE = 2;
public static final int EVENT_VARIABLE_CHANGE = 3; public static final int EVENT_VARIABLE_CHANGE = 3;
......
package emu.grasscutter.scripts.data; package emu.grasscutter.scripts.data;
import java.util.List; import com.github.davidmoten.rtreemulti.RTree;
import com.github.davidmoten.rtreemulti.geometry.Geometry;
import com.github.davidmoten.rtreemulti.geometry.Rectangle;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.scripts.SceneIndexManager;
import emu.grasscutter.scripts.ScriptLoader;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import lombok.Setter;
import lombok.ToString;
import javax.script.Bindings;
import javax.script.CompiledScript;
import javax.script.ScriptException;
import java.util.Map;
import java.util.stream.Collectors;
import static emu.grasscutter.Configuration.SCRIPT;
@ToString
@Setter
public class SceneBlock { public class SceneBlock {
public int id; public int id;
public Position max; public Position max;
public Position min; public Position min;
public List<SceneGroup> groups;
public int sceneId;
public Map<Integer,SceneGroup> groups;
public RTree<SceneGroup, Geometry> sceneGroupIndex;
private transient boolean loaded; // Not an actual variable in the scripts either
public boolean isLoaded() {
return loaded;
}
public void setLoaded(boolean loaded) {
this.loaded = loaded;
}
public boolean contains(Position pos) { public boolean contains(Position pos) {
return pos.getX() <= max.getX() && pos.getX() >= min.getX() && return pos.getX() <= max.getX() && pos.getX() >= min.getX() &&
pos.getZ() <= max.getZ() && pos.getZ() >= min.getZ(); pos.getZ() <= max.getZ() && pos.getZ() >= min.getZ();
} }
}
public SceneBlock load(int sceneId, Bindings bindings){
if(loaded){
return this;
}
this.sceneId = sceneId;
setLoaded(true);
CompiledScript cs = ScriptLoader.getScriptByPath(
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_block" + id + "." + ScriptLoader.getScriptType()));
if (cs == null) {
return null;
}
// Eval script
try {
cs.eval(bindings);
// Set groups
groups = ScriptLoader.getSerializer().toList(SceneGroup.class, bindings.get("groups")).stream()
.collect(Collectors.toMap(x -> x.id, y -> y));
groups.values().forEach(g -> g.block_id = id);
this.sceneGroupIndex = SceneIndexManager.buildIndex(3, groups.values(), g -> g.pos.toPoint());
} catch (ScriptException e) {
Grasscutter.getLogger().error("Error loading block " + id + " in scene " + sceneId, e);
}
Grasscutter.getLogger().info("scene {} block {} is loaded successfully.", sceneId, id);
return this;
}
public Rectangle toRectangle() {
return Rectangle.create(min.toXZDoubleArray(), max.toXZDoubleArray());
}
}
\ No newline at end of file
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;
}
package emu.grasscutter.scripts.data;
import lombok.Setter;
import lombok.ToString;
@ToString
@Setter
public class SceneBusiness {
public int type;
}
package emu.grasscutter.scripts.data; package emu.grasscutter.scripts.data;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import lombok.Setter;
import lombok.ToString;
@ToString
@Setter
public class SceneConfig { public class SceneConfig {
public Position vision_anchor; public Position vision_anchor;
public Position born_pos; public Position born_pos;
......
package emu.grasscutter.scripts.data; package emu.grasscutter.scripts.data;
import emu.grasscutter.utils.Position; import lombok.Setter;
import lombok.ToString;
public class SceneGadget { @ToString
public int level; @Setter
public int config_id; public class SceneGadget extends SceneObject{
public int gadget_id; public int gadget_id;
public int state; public int state;
public Position pos; public int point_type;
public Position rot; public SceneBossChest boss_chest;
public int interact_id;
} }
package emu.grasscutter.scripts.data;
import java.util.List;
import lombok.Setter;
import lombok.ToString;
@ToString
@Setter
public class SceneGarbage {
public List<SceneGadget> gadgets;
}
package emu.grasscutter.scripts.data; package emu.grasscutter.scripts.data;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.scripts.ScriptLoader;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import lombok.Setter;
import lombok.ToString;
import javax.script.Bindings;
import javax.script.CompiledScript;
import javax.script.ScriptException;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import static emu.grasscutter.Configuration.SCRIPT;
@ToString
@Setter
public class SceneGroup { public class SceneGroup {
public transient int block_id; // Not an actual variable in the scripts but we will keep it here for reference public transient int block_id; // Not an actual variable in the scripts but we will keep it here for reference
...@@ -12,27 +29,140 @@ public class SceneGroup { ...@@ -12,27 +29,140 @@ public class SceneGroup {
public int refresh_id; public int refresh_id;
public Position pos; public Position pos;
/** public Map<Integer,SceneMonster> monsters; // <ConfigId, Monster>
* ConfigId - Monster public Map<Integer, SceneGadget> gadgets; // <ConfigId, Gadgets>
*/ public Map<String, SceneTrigger> triggers;
public Map<Integer,SceneMonster> monsters; public Map<Integer, SceneNPC> npc; // <NpcId, NPC>
public List<SceneGadget> gadgets;
public List<SceneTrigger> triggers;
public List<SceneRegion> regions; public List<SceneRegion> regions;
public List<SceneSuite> suites; public List<SceneSuite> suites;
public SceneInitConfig init_config; public List<SceneVar> variables;
private transient boolean isLoaded; // Not an actual variable in the scripts either public SceneBusiness business;
public SceneGarbage garbages;
public SceneInitConfig init_config;
private transient boolean loaded; // Not an actual variable in the scripts either
private transient CompiledScript script;
private transient Bindings bindings;
public static SceneGroup of(int groupId) {
var group = new SceneGroup();
group.id = groupId;
return group;
}
public boolean isLoaded() { public boolean isLoaded() {
return isLoaded; return loaded;
} }
public boolean setLoaded(boolean loaded) { public void setLoaded(boolean loaded) {
return loaded; this.loaded = loaded;
}
public int getBusinessType() {
return this.business == null ? 0 : this.business.type;
} }
public List<SceneGadget> getGarbageGadgets() {
return this.garbages == null ? null : this.garbages.gadgets;
}
public CompiledScript getScript() {
return script;
}
public SceneSuite getSuiteByIndex(int index) { public SceneSuite getSuiteByIndex(int index) {
return suites.get(index - 1); return suites.get(index - 1);
} }
public Bindings getBindings() {
return bindings;
}
public synchronized SceneGroup load(int sceneId){
if(loaded){
return this;
}
// Set flag here so if there is no script, we dont call this function over and over again.
setLoaded(true);
this.bindings = ScriptLoader.getEngine().createBindings();
CompiledScript cs = ScriptLoader.getScriptByPath(
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_group" + id + "." + ScriptLoader.getScriptType()));
if (cs == null) {
return this;
}
this.script = cs;
// Eval script
try {
cs.eval(bindings);
// Set
monsters = ScriptLoader.getSerializer().toList(SceneMonster.class, bindings.get("monsters")).stream()
.collect(Collectors.toMap(x -> x.config_id, y -> y));
monsters.values().forEach(m -> m.group = this);
gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, bindings.get("gadgets")).stream()
.collect(Collectors.toMap(x -> x.config_id, y -> y));
gadgets.values().forEach(m -> m.group = this);
triggers = ScriptLoader.getSerializer().toList(SceneTrigger.class, bindings.get("triggers")).stream()
.collect(Collectors.toMap(x -> x.name, y -> y));
triggers.values().forEach(t -> t.currentGroup = this);
suites = ScriptLoader.getSerializer().toList(SceneSuite.class, bindings.get("suites"));
regions = ScriptLoader.getSerializer().toList(SceneRegion.class, bindings.get("regions"));
init_config = ScriptLoader.getSerializer().toObject(SceneInitConfig.class, bindings.get("init_config"));
// Garbages TODO fix properly later
Object garbagesValue = bindings.get("garbages");
if (garbagesValue != null && garbagesValue instanceof LuaValue garbagesTable) {
garbages = new SceneGarbage();
if (garbagesTable.checktable().get("gadgets") != LuaValue.NIL) {
garbages.gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, garbagesTable.checktable().get("gadgets").checktable());
garbages.gadgets.forEach(m -> m.group = this);
}
}
// Add variables to suite
variables = ScriptLoader.getSerializer().toList(SceneVar.class, bindings.get("variables"));
// NPC in groups
npc = ScriptLoader.getSerializer().toList(SceneNPC.class, bindings.get("npcs")).stream()
.collect(Collectors.toMap(x -> x.npc_id, y -> y));
npc.values().forEach(n -> n.group = this);
// Add monsters and gadgets to suite
for (SceneSuite suite : suites) {
suite.sceneMonsters = new ArrayList<>(
suite.monsters.stream()
.filter(monsters::containsKey)
.map(monsters::get)
.toList()
);
suite.sceneGadgets = new ArrayList<>(
suite.gadgets.stream()
.filter(gadgets::containsKey)
.map(gadgets::get)
.toList()
);
suite.sceneTriggers = new ArrayList<>(
suite.triggers.stream()
.filter(triggers::containsKey)
.map(triggers::get)
.toList()
);
}
} catch (ScriptException e) {
Grasscutter.getLogger().error("Error loading group " + id + " in scene " + sceneId, e);
}
Grasscutter.getLogger().info("group {} in scene {} is loaded successfully.", id, sceneId);
return this;
}
} }
package emu.grasscutter.scripts.data; package emu.grasscutter.scripts.data;
import emu.grasscutter.utils.Position; import lombok.Setter;
import lombok.ToString;
@ToString
@Setter
public class SceneInitConfig { public class SceneInitConfig {
public int suite; public int suite;
public int end_suite; public int end_suite;
......
package emu.grasscutter.scripts.data;
import com.github.davidmoten.rtreemulti.RTree;
import com.github.davidmoten.rtreemulti.geometry.Geometry;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.scripts.SceneIndexManager;
import emu.grasscutter.scripts.ScriptLoader;
import lombok.Setter;
import lombok.ToString;
import javax.script.Bindings;
import javax.script.CompiledScript;
import javax.script.ScriptException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static emu.grasscutter.Configuration.SCRIPT;
@ToString
@Setter
public class SceneMeta {
public SceneConfig config;
public Map<Integer, SceneBlock> blocks;
public Bindings context;
public RTree<SceneBlock, Geometry> sceneBlockIndex;
public static SceneMeta of(int sceneId) {
return new SceneMeta().load(sceneId);
}
public SceneMeta load(int sceneId){
// Get compiled script if cached
CompiledScript cs = ScriptLoader.getScriptByPath(
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "." + ScriptLoader.getScriptType()));
if (cs == null) {
Grasscutter.getLogger().warn("No script found for scene " + sceneId);
return null;
}
// Create bindings
context = ScriptLoader.getEngine().createBindings();
// Eval script
try {
cs.eval(context);
this.config = ScriptLoader.getSerializer().toObject(SceneConfig.class, context.get("scene_config"));
// TODO optimize later
// Create blocks
List<Integer> blockIds = ScriptLoader.getSerializer().toList(Integer.class, context.get("blocks"));
List<SceneBlock> blocks = ScriptLoader.getSerializer().toList(SceneBlock.class, context.get("block_rects"));
for (int i = 0; i < blocks.size(); i++) {
SceneBlock block = blocks.get(i);
block.id = blockIds.get(i);
}
this.blocks = blocks.stream().collect(Collectors.toMap(b -> b.id, b -> b));
this.sceneBlockIndex = SceneIndexManager.buildIndex(2, blocks, SceneBlock::toRectangle);
} catch (ScriptException e) {
Grasscutter.getLogger().error("Error running script", e);
return null;
}
Grasscutter.getLogger().info("scene {} metadata is loaded successfully.", sceneId);
return this;
}
}
package emu.grasscutter.scripts.data; package emu.grasscutter.scripts.data;
import emu.grasscutter.utils.Position; import lombok.Setter;
import lombok.ToString;
public class SceneMonster { @ToString
public int level; @Setter
public int config_id; public class SceneMonster extends SceneObject{
public int monster_id; public int monster_id;
public Position pos; public int pose_id;
public Position rot; public int drop_id;
} }
\ No newline at end of file
package emu.grasscutter.scripts.data;
import lombok.Setter;
import lombok.ToString;
@ToString
@Setter
public class SceneNPC extends SceneObject{
public int npc_id;
}
\ No newline at end of file
package emu.grasscutter.scripts.data;
import emu.grasscutter.utils.Position;
import lombok.Setter;
import lombok.ToString;
@ToString
@Setter
public class SceneObject {
public int level;
public int config_id;
public int area_id;
public Position pos;
public Position rot;
/**
* not set by lua
*/
public transient SceneGroup group;
}
...@@ -5,7 +5,12 @@ import emu.grasscutter.scripts.constants.ScriptRegionShape; ...@@ -5,7 +5,12 @@ import emu.grasscutter.scripts.constants.ScriptRegionShape;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.ints.IntSet;
import lombok.Data;
import lombok.Setter;
import lombok.ToString;
@ToString
@Setter
public class SceneRegion { public class SceneRegion {
public int config_id; public int config_id;
public int shape; public int shape;
......
...@@ -2,8 +2,11 @@ package emu.grasscutter.scripts.data; ...@@ -2,8 +2,11 @@ package emu.grasscutter.scripts.data;
import java.util.List; import java.util.List;
import emu.grasscutter.utils.Position; import lombok.Setter;
import lombok.ToString;
@ToString
@Setter
public class SceneSuite { public class SceneSuite {
public List<Integer> monsters; public List<Integer> monsters;
public List<Integer> gadgets; public List<Integer> gadgets;
...@@ -12,4 +15,5 @@ public class SceneSuite { ...@@ -12,4 +15,5 @@ public class SceneSuite {
public transient List<SceneMonster> sceneMonsters; public transient List<SceneMonster> sceneMonsters;
public transient List<SceneGadget> sceneGadgets; public transient List<SceneGadget> sceneGadgets;
public transient List<SceneTrigger> sceneTriggers;
} }
package emu.grasscutter.scripts.data; package emu.grasscutter.scripts.data;
import lombok.Setter;
@Setter
public class SceneTrigger { public class SceneTrigger {
public String name; public String name;
public int config_id; public int config_id;
...@@ -8,6 +11,7 @@ public class SceneTrigger { ...@@ -8,6 +11,7 @@ public class SceneTrigger {
public String condition; public String condition;
public String action; public String action;
public transient SceneGroup currentGroup;
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if(obj instanceof SceneTrigger sceneTrigger){ if(obj instanceof SceneTrigger sceneTrigger){
......
package emu.grasscutter.scripts.data; package emu.grasscutter.scripts.data;
import lombok.Setter;
import lombok.ToString;
@ToString
@Setter
public class SceneVar { public class SceneVar {
public String name; public String name;
public int value; public int value;
......
package emu.grasscutter.scripts.serializer; package emu.grasscutter.scripts.serializer;
import java.lang.reflect.Field; import com.esotericsoftware.reflectasm.ConstructorAccess;
import java.util.ArrayList; import com.esotericsoftware.reflectasm.MethodAccess;
import java.util.List;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.scripts.ScriptUtils;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.experimental.FieldDefaults;
import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class LuaSerializer implements Serializer { public class LuaSerializer implements Serializer {
private final static Map<Class<?>, MethodAccess> methodAccessCache = new ConcurrentHashMap<>();
private final static Map<Class<?>, ConstructorAccess<?>> constructorCache = new ConcurrentHashMap<>();
private final static Map<Class<?>, Map<String, FieldMeta>> fieldMetaCache = new ConcurrentHashMap<>();
@Override @Override
public <T> List<T> toList(Class<T> type, Object obj) { public <T> List<T> toList(Class<T> type, Object obj) {
return serializeList(type, (LuaTable) obj); return serializeList(type, (LuaTable) obj);
...@@ -20,7 +34,11 @@ public class LuaSerializer implements Serializer { ...@@ -20,7 +34,11 @@ public class LuaSerializer implements Serializer {
} }
public <T> List<T> serializeList(Class<T> type, LuaTable table) { public <T> List<T> serializeList(Class<T> type, LuaTable table) {
List<T> list = new ArrayList(); List<T> list = new ArrayList<>();
if (table == null) {
return list;
}
try { try {
LuaValue[] keys = table.keys(); LuaValue[] keys = table.keys();
...@@ -55,7 +73,7 @@ public class LuaSerializer implements Serializer { ...@@ -55,7 +73,7 @@ public class LuaSerializer implements Serializer {
return list; return list;
} }
public <T> T serialize(Class<T> type, LuaTable table) { public <T> T serialize(Class<T> type, LuaTable table) {
T object = null; T object = null;
...@@ -70,27 +88,38 @@ public class LuaSerializer implements Serializer { ...@@ -70,27 +88,38 @@ public class LuaSerializer implements Serializer {
} }
try { try {
//noinspection ConfusingArgumentToVarargsMethod if (!methodAccessCache.containsKey(type)) {
object = type.getDeclaredConstructor().newInstance(); cacheType(type);
}
var methodAccess = methodAccessCache.get(type);
var fieldMetaMap = fieldMetaCache.get(type);
object = (T) constructorCache.get(type).newInstance();
if (table == null) {
return object;
}
LuaValue[] keys = table.keys(); LuaValue[] keys = table.keys();
for (LuaValue k : keys) { for (LuaValue k : keys) {
try { try {
Field field = object.getClass().getDeclaredField(k.checkjstring()); var keyName = k.checkjstring();
if(!fieldMetaMap.containsKey(keyName)){
field.setAccessible(true); continue;
}
var fieldMeta = fieldMetaMap.get(keyName);
LuaValue keyValue = table.get(k); LuaValue keyValue = table.get(k);
if (keyValue.istable()) { if (keyValue.istable()) {
field.set(object, serialize(field.getType(), keyValue.checktable())); methodAccess.invoke(object, fieldMeta.index, serialize(fieldMeta.getType(), keyValue.checktable()));
} else if (field.getType().equals(float.class)) { } else if (fieldMeta.getType().equals(float.class)) {
field.setFloat(object, keyValue.tofloat()); methodAccess.invoke(object, fieldMeta.index, keyValue.tofloat());
} else if (field.getType().equals(int.class)) { } else if (fieldMeta.getType().equals(int.class)) {
field.setInt(object, keyValue.toint()); methodAccess.invoke(object, fieldMeta.index, keyValue.toint());
} else if (field.getType().equals(String.class)) { } else if (fieldMeta.getType().equals(String.class)) {
field.set(object, keyValue.tojstring()); methodAccess.invoke(object, fieldMeta.index, keyValue.tojstring());
} else { } else {
field.set(object, keyValue); methodAccess.invoke(object, fieldMeta.index, keyValue.tojstring());
} }
} catch (Exception ex) { } catch (Exception ex) {
//ex.printStackTrace(); //ex.printStackTrace();
...@@ -98,9 +127,64 @@ public class LuaSerializer implements Serializer { ...@@ -98,9 +127,64 @@ public class LuaSerializer implements Serializer {
} }
} }
} catch (Exception e) { } catch (Exception e) {
Grasscutter.getLogger().info(ScriptUtils.toMap(table).toString());
e.printStackTrace(); e.printStackTrace();
} }
return object; return object;
} }
public <T> Map<String, FieldMeta> cacheType(Class<T> type){
if(fieldMetaCache.containsKey(type)) {
return fieldMetaCache.get(type);
}
if(!constructorCache.containsKey(type)){
constructorCache.putIfAbsent(type, ConstructorAccess.get(type));
}
var methodAccess = Optional.ofNullable(methodAccessCache.get(type)).orElse(MethodAccess.get(type));
methodAccessCache.putIfAbsent(type, methodAccess);
var fieldMetaMap = new HashMap<String, FieldMeta>();
var methodNameSet = new HashSet<>(Arrays.stream(methodAccess.getMethodNames()).toList());
Arrays.stream(type.getDeclaredFields())
.filter(field -> methodNameSet.contains(getSetterName(field.getName())))
.forEach(field -> {
var setter = getSetterName(field.getName());
var index = methodAccess.getIndex(setter);
fieldMetaMap.put(field.getName(), new FieldMeta(field.getName(), setter, index, field.getType()));
});
Arrays.stream(type.getFields())
.filter(field -> !fieldMetaMap.containsKey(field.getName()))
.filter(field -> methodNameSet.contains(getSetterName(field.getName())))
.forEach(field -> {
var setter = getSetterName(field.getName());
var index = methodAccess.getIndex(setter);
fieldMetaMap.put(field.getName(), new FieldMeta(field.getName(), setter, index, field.getType()));
});
fieldMetaCache.put(type, fieldMetaMap);
return fieldMetaMap;
}
public String getSetterName(String fieldName){
if(fieldName == null || fieldName.length() == 0){
return null;
}
if(fieldName.length() == 1){
return "set" + fieldName.toUpperCase();
}
return "set" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
}
@Data
@AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
static class FieldMeta{
String name;
String setter;
int index;
Class<?> type;
}
} }
...@@ -16,9 +16,9 @@ import java.util.List; ...@@ -16,9 +16,9 @@ import java.util.List;
public class ScriptMonsterSpawnService { public class ScriptMonsterSpawnService {
private final SceneScriptManager sceneScriptManager; private final SceneScriptManager sceneScriptManager;
private final List<ScriptMonsterListener> onMonsterCreatedListener = new ArrayList<>(); public final List<ScriptMonsterListener> onMonsterCreatedListener = new ArrayList<>();
private final List<ScriptMonsterListener> onMonsterDeadListener = new ArrayList<>(); public final List<ScriptMonsterListener> onMonsterDeadListener = new ArrayList<>();
public ScriptMonsterSpawnService(SceneScriptManager sceneScriptManager){ public ScriptMonsterSpawnService(SceneScriptManager sceneScriptManager){
this.sceneScriptManager = sceneScriptManager; this.sceneScriptManager = sceneScriptManager;
...@@ -39,40 +39,5 @@ public class ScriptMonsterSpawnService { ...@@ -39,40 +39,5 @@ public class ScriptMonsterSpawnService {
public void onMonsterDead(EntityMonster entityMonster){ public void onMonsterDead(EntityMonster entityMonster){
onMonsterDeadListener.forEach(l -> l.onNotify(entityMonster)); onMonsterDeadListener.forEach(l -> l.onNotify(entityMonster));
} }
public void spawnMonster(int groupId, SceneMonster monster) {
if(monster == null){
return;
}
MonsterData data = GameData.getMonsterDataMap().get(monster.monster_id);
if (data == null) {
return;
}
// Calculate level
int level = monster.level;
if (sceneScriptManager.getScene().getDungeonData() != null) {
level = sceneScriptManager.getScene().getDungeonData().getShowLevel();
} else if (sceneScriptManager.getScene().getWorld().getWorldLevel() > 0) {
WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(sceneScriptManager.getScene().getWorld().getWorldLevel());
if (worldLevelData != null) {
level = worldLevelData.getMonsterLevel();
}
}
// Spawn mob
EntityMonster entity = new EntityMonster(sceneScriptManager.getScene(), data, monster.pos, level);
entity.getRotation().set(monster.rot);
entity.setGroupId(groupId);
entity.setConfigId(monster.config_id);
onMonsterCreatedListener.forEach(action -> action.onNotify(entity));
sceneScriptManager.getScene().addEntity(entity);
sceneScriptManager.callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(entity.getConfigId()));
}
} }
Markdown is supported
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