SceneScriptManager.java 10.9 KB
Newer Older
1
2
package emu.grasscutter.scripts;

Akka's avatar
Akka committed
3
import ch.ethz.globis.phtree.PhTree;
4
import emu.grasscutter.Grasscutter;
Akka's avatar
Akka committed
5
6
7
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.MonsterData;
import emu.grasscutter.data.def.WorldLevelData;
8
import emu.grasscutter.game.entity.EntityGadget;
Akka's avatar
Akka committed
9
10
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.entity.GameEntity;
11
import emu.grasscutter.game.world.Scene;
Akka's avatar
Akka committed
12
import emu.grasscutter.net.proto.VisionTypeOuterClass;
Melledy's avatar
Melledy committed
13
import emu.grasscutter.scripts.constants.EventType;
Akka's avatar
Akka committed
14
15
16
import emu.grasscutter.scripts.data.*;
import emu.grasscutter.scripts.service.ScriptMonsterSpawnService;
import emu.grasscutter.scripts.service.ScriptMonsterTideService;
17
18
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
Akka's avatar
Akka committed
19
20
21
import org.luaj.vm2.LuaError;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
22

Akka's avatar
Akka committed
23
import java.util.*;
24

25
26
public class SceneScriptManager {
	private final Scene scene;
Melledy's avatar
Melledy committed
27
	private final Map<String, Integer> variables;
Akka's avatar
Akka committed
28
	private SceneMeta meta;
29
	private boolean isInit;
30
31
32
33
34
35
36
37
	/**
	 * SceneTrigger Set
	 */
	private final Map<String, SceneTrigger> triggers;
	/**
	 * current triggers controlled by RefreshGroup
	 */
	private final Int2ObjectOpenHashMap<Set<SceneTrigger>> currentTriggers;
Melledy's avatar
Melledy committed
38
	private final Int2ObjectOpenHashMap<SceneRegion> regions;
39
40
41
	private Map<Integer,SceneGroup> sceneGroups;
	private ScriptMonsterTideService scriptMonsterTideService;
	private ScriptMonsterSpawnService scriptMonsterSpawnService;
Akka's avatar
Akka committed
42
43
44
45
	/**
	 * blockid - loaded groupSet
	 */
	private Int2ObjectMap<Set<SceneGroup>> loadedGroupSetPerBlock;
46
47
	public SceneScriptManager(Scene scene) {
		this.scene = scene;
48
49
50
		this.triggers = new HashMap<>();
		this.currentTriggers = new Int2ObjectOpenHashMap<>();

Melledy's avatar
Melledy committed
51
		this.regions = new Int2ObjectOpenHashMap<>();
52
		this.variables = new HashMap<>();
53
54
		this.sceneGroups = new HashMap<>();
		this.scriptMonsterSpawnService = new ScriptMonsterSpawnService(this);
Akka's avatar
Akka committed
55
		this.loadedGroupSetPerBlock = new Int2ObjectOpenHashMap<>();
56

57
		// TEMPORARY
Akka's avatar
Akka committed
58
		if (this.getScene().getId() < 10 && !Grasscutter.getConfig().server.game.enableScriptInBigWorld) {
59
60
61
62
63
64
65
66
67
68
69
70
			return;
		}
		
		// Create
		this.init();
	}
	
	public Scene getScene() {
		return scene;
	}

	public SceneConfig getConfig() {
Akka's avatar
Akka committed
71
72
73
74
		if(!isInit){
			return null;
		}
		return meta.config;
75
76
	}

Akka's avatar
Akka committed
77
78
	public Map<Integer, SceneBlock> getBlocks() {
		return meta.blocks;
79
80
	}

Melledy's avatar
Melledy committed
81
	public Map<String, Integer> getVariables() {
82
83
84
		return variables;
	}

85
	public Set<SceneTrigger> getTriggersByEvent(int eventId) {
86
		return currentTriggers.computeIfAbsent(eventId, e -> new HashSet<>());
87
88
	}
	public void registerTrigger(SceneTrigger trigger) {
89
		this.triggers.put(trigger.name, trigger);
90
91
92
93
		getTriggersByEvent(trigger.event).add(trigger);
	}
	
	public void deregisterTrigger(SceneTrigger trigger) {
94
		this.triggers.remove(trigger.name);
95
96
		getTriggersByEvent(trigger.event).remove(trigger);
	}
Akka's avatar
Akka committed
97
98
	public void resetTriggers(int eventId) {
		currentTriggers.put(eventId, new HashSet<>());
99
100
101
102
103
104
	}
	public void refreshGroup(SceneGroup group, int suiteIndex){
		var suite = group.getSuiteByIndex(suiteIndex);
		if(suite == null){
			return;
		}
Akka's avatar
Akka committed
105
106
107
108
109
		if(suite.sceneTriggers.size() > 0){
			for(var trigger : suite.sceneTriggers){
				resetTriggers(trigger.event);
				this.currentTriggers.get(trigger.event).add(trigger);
			}
110
111
112
113
		}
		spawnMonstersInGroup(group, suite);
		spawnGadgetsInGroup(group, suite);
	}
Melledy's avatar
Melledy committed
114
115
116
117
118
119
120
121
122
123
124
	public SceneRegion getRegionById(int id) {
		return regions.get(id);
	}
	
	public void registerRegion(SceneRegion region) {
		regions.put(region.config_id, region);
	}
	
	public void deregisterRegion(SceneRegion region) {
		regions.remove(region.config_id);
	}
Akka's avatar
Akka committed
125
126
127
128
129

	public Int2ObjectMap<Set<SceneGroup>> getLoadedGroupSetPerBlock() {
		return loadedGroupSetPerBlock;
	}

130
131
132
133
134
	// TODO optimize
	public SceneGroup getGroupById(int groupId) {
		for (SceneBlock block : this.getScene().getLoadedBlocks()) {
			for (SceneGroup group : block.groups) {
				if (group.id == groupId) {
Akka's avatar
Akka committed
135
					if(!group.isLoaded()){
Akka's avatar
Akka committed
136
						getScene().onLoadGroup(List.of(group));
Akka's avatar
Akka committed
137
					}
138
139
140
141
142
143
144
145
					return group;
				}
			}
		}
		return null;
	}

	private void init() {
Akka's avatar
Akka committed
146
147
		var meta = ScriptLoader.getSceneMeta(getScene().getId());
		if (meta == null){
148
			return;
149
		}
Akka's avatar
Akka committed
150
151
		this.meta = meta;

152
153
154
155
156
157
158
159
		// TEMP
		this.isInit = true;
	}

	public boolean isInit() {
		return isInit;
	}
	
Akka's avatar
Akka committed
160
161
	public void loadBlockFromScript(SceneBlock block) {
		block.load(scene.getId(), meta.context);
162
163
	}
	
164
	public void loadGroupFromScript(SceneGroup group) {
165
		group.load(getScene().getId());
166

Akka's avatar
Akka committed
167
168
169
170
171
172
		group.variables.forEach(var -> this.getVariables().put(var.name, var.value));
		this.sceneGroups.put(group.id, group);

		if(group.regions != null){
			group.regions.forEach(this::registerRegion);
		}
173
174
	}
	
Melledy's avatar
Melledy committed
175
176
177
178
179
180
181
182
183
184
	public void checkRegions() {
		if (this.regions.size() == 0) {
			return;
		}
		
		for (SceneRegion region : this.regions.values()) {
			getScene().getEntities().values()
				.stream()
				.filter(e -> e.getEntityType() <= 2 && region.contains(e.getPosition()))
				.forEach(region::addEntity);
185

Melledy's avatar
Melledy committed
186
187
188
189
190
191
192
			if (region.hasNewEntities()) {
				// This is not how it works, source_eid should be region entity id, but we dont have an entity for regions yet
				callEvent(EventType.EVENT_ENTER_REGION, new ScriptArgs(region.config_id).setSourceEntityId(region.config_id));
				
				region.resetNewEntities();
			}
		}
193
194
	}
	
195
196
197
198
	public void spawnGadgetsInGroup(SceneGroup group, int suiteIndex) {
		spawnGadgetsInGroup(group, group.getSuiteByIndex(suiteIndex));
	}
	
199
	public void spawnGadgetsInGroup(SceneGroup group) {
200
201
202
203
		spawnGadgetsInGroup(group, null);
	}
	
	public void spawnGadgetsInGroup(SceneGroup group, SceneSuite suite) {
Akka's avatar
Akka committed
204
		var gadgets = group.gadgets.values();
205
206
207
208
		
		if (suite != null) {
			gadgets = suite.sceneGadgets;
		}
Akka's avatar
Akka committed
209
210

		var toCreate = gadgets.stream()
Akka's avatar
Akka committed
211
				.map(g -> createGadget(g.groupId, group.block_id, g))
Akka's avatar
Akka committed
212
213
214
				.filter(Objects::nonNull)
				.toList();
		this.addEntities(toCreate);
Melledy's avatar
Melledy committed
215
	}
216

Melledy's avatar
Melledy committed
217
	public void spawnMonstersInGroup(SceneGroup group, int suiteIndex) {
218
219
220
221
		var suite = group.getSuiteByIndex(suiteIndex);
		if(suite == null){
			return;
		}
222
223
224
225
226
		spawnMonstersInGroup(group, suite);
	}
	public void spawnMonstersInGroup(SceneGroup group, SceneSuite suite) {
		if(suite == null || suite.sceneMonsters.size() <= 0){
			return;
227
		}
Akka's avatar
Akka committed
228
229
230
		this.addEntities(suite.sceneMonsters.stream()
				.map(mob -> createMonster(group.id, group.block_id, mob)).toList());

Melledy's avatar
Melledy committed
231
232
	}
	
Melledy's avatar
Melledy committed
233
	public void spawnMonstersInGroup(SceneGroup group) {
Akka's avatar
Akka committed
234
235
		this.addEntities(group.monsters.values().stream()
				.map(mob -> createMonster(group.id, group.block_id, mob)).toList());
Melledy's avatar
Melledy committed
236
	}
237

238
239
240
	public void startMonsterTideInGroup(SceneGroup group, Integer[] ordersConfigId, int tideCount, int sceneLimit) {
		this.scriptMonsterTideService =
				new ScriptMonsterTideService(this, group, tideCount, sceneLimit, ordersConfigId);
241
242

	}
243
244
245
246
247
248
	public void unloadCurrentMonsterTide(){
		if(this.getScriptMonsterTideService() == null){
			return;
		}
		this.getScriptMonsterTideService().unload();
	}
Akka's avatar
Akka committed
249
	public void spawnMonstersByConfigId(SceneGroup group, int configId, int delayTime) {
250
		// TODO delay
Akka's avatar
Akka committed
251
		getScene().addEntity(createMonster(group.id, group.block_id, group.monsters.get(configId)));
Melledy's avatar
Melledy committed
252
	}
253
254
	// Events
	
Melledy's avatar
Melledy committed
255
	public void callEvent(int eventType, ScriptArgs params) {
256
257
258
259
260
		try{
			ScriptLoader.getScriptLib().setSceneScriptManager(this);
			for (SceneTrigger trigger : this.getTriggersByEvent(eventType)) {
				try{
					ScriptLoader.getScriptLib().setCurrentGroup(trigger.currentGroup);
261

262
263
264
265
266
267
268
269
270
271
272
273
					LuaValue ret = callScriptFunc(trigger.condition, trigger.currentGroup, params);
					Grasscutter.getLogger().trace("Call Condition Trigger {}", trigger.condition);

					if (ret.isboolean() && ret.checkboolean()) {
						// the SetGroupVariableValueByGroup in tower need the param to record the first stage time
						callScriptFunc(trigger.action, trigger.currentGroup, params);
						Grasscutter.getLogger().trace("Call Action Trigger {}", trigger.action);
					}
					//TODO some ret may not bool

				}finally {
					ScriptLoader.getScriptLib().removeCurrentGroup();
Akka's avatar
Akka committed
274
				}
275
			}
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
		}finally {
			// make sure it is removed
			ScriptLoader.getScriptLib().removeSceneScriptManager();
		}
	}

	public LuaValue callScriptFunc(String funcName, SceneGroup group, ScriptArgs params){
		LuaValue funcLua = null;
		if (funcName != null && !funcName.isEmpty()) {
			funcLua = (LuaValue) group.getBindings().get(funcName);
		}

		LuaValue ret = LuaValue.TRUE;

		if (funcLua != null) {
			LuaValue args = LuaValue.NIL;

			if (params != null) {
				args = CoerceJavaToLua.coerce(params);
			}

			ret = safetyCall(funcName, funcLua, args);
298
		}
299
		return ret;
300
	}
301

302
303
	public LuaValue safetyCall(String name, LuaValue func, LuaValue args){
		try{
304
			return func.call(ScriptLoader.getScriptLibLua(), args);
305
		}catch (LuaError error){
Akka's avatar
Akka committed
306
			ScriptLib.logger.error("[LUA] call trigger failed {},{},{}",name,args,error.getMessage());
307
308
309
310
311
312
313
314
315
316
317
318
			return LuaValue.valueOf(-1);
		}
	}

	public ScriptMonsterTideService getScriptMonsterTideService() {
		return scriptMonsterTideService;
	}

	public ScriptMonsterSpawnService getScriptMonsterSpawnService() {
		return scriptMonsterSpawnService;
	}

Akka's avatar
Akka committed
319
	public EntityGadget createGadget(int groupId, int blockId, SceneGadget g) {
Akka's avatar
Akka committed
320
321
322
323
324
325
326
327
328
329
330
		EntityGadget entity = new EntityGadget(getScene(), g.gadget_id, g.pos);

		if (entity.getGadgetData() == null){
			return null;
		}

		entity.setBlockId(blockId);
		entity.setConfigId(g.config_id);
		entity.setGroupId(groupId);
		entity.getRotation().set(g.rot);
		entity.setState(g.state);
Melledy's avatar
Melledy committed
331
332
		entity.setPointType(g.point_type);
		entity.buildContent();
333
334
335
		
		// Lua event
		this.callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(entity.getConfigId()));
Akka's avatar
Akka committed
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372

		return entity;
	}

	public EntityMonster createMonster(int groupId, int blockId, SceneMonster monster) {
		if(monster == null){
			return null;
		}

		MonsterData data = GameData.getMonsterDataMap().get(monster.monster_id);

		if (data == null) {
			return null;
		}

		// Calculate level
		int level = monster.level;

		if (getScene().getDungeonData() != null) {
			level = getScene().getDungeonData().getShowLevel();
		} else if (getScene().getWorld().getWorldLevel() > 0) {
			WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(getScene().getWorld().getWorldLevel());

			if (worldLevelData != null) {
				level = worldLevelData.getMonsterLevel();
			}
		}

		// Spawn mob
		EntityMonster entity = new EntityMonster(getScene(), data, monster.pos, level);
		entity.getRotation().set(monster.rot);
		entity.setGroupId(groupId);
		entity.setBlockId(blockId);
		entity.setConfigId(monster.config_id);

		this.getScriptMonsterSpawnService()
				.onMonsterCreatedListener.forEach(action -> action.onNotify(entity));
373
		
Akka's avatar
Akka committed
374
375
376
377
378
379
		return entity;
	}

	public void addEntity(GameEntity gameEntity){
		getScene().addEntity(gameEntity);
	}
380
	
Akka's avatar
Akka committed
381
382
383
	public void meetEntities(List<? extends GameEntity> gameEntity){
		getScene().addEntities(gameEntity, VisionTypeOuterClass.VisionType.VISION_MEET);
	}
384
	
Akka's avatar
Akka committed
385
386
387
388
389
390
391
	public void addEntities(List<? extends GameEntity> gameEntity){
		getScene().addEntities(gameEntity);
	}

	public PhTree<SceneBlock> getBlocksIndex() {
		return meta.sceneBlockIndex;
	}
392
}