Commit c2a7d607 authored by Akka's avatar Akka Committed by GitHub
Browse files

Merge pull request #9 from Grasscutters/development

Development
parents 0bfa1fa0 9a7d78e0
...@@ -10,7 +10,7 @@ import java.util.List; ...@@ -10,7 +10,7 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
@Command(label = "setworldlevel", usage = "setworldlevel <level>", @Command(label = "setworldlevel", usage = "setworldlevel <level>",
aliases = {"setworldlvl"}, permission = "player.setworldlevel", description = "commands.setWorldLevel.description") aliases = {"setworldlvl"}, permission = "player.setworldlevel", permissionTargeted = "player.setworldlevel.others", description = "commands.setWorldLevel.description")
public final class SetWorldLevelCommand implements CommandHandler { public final class SetWorldLevelCommand implements CommandHandler {
@Override @Override
......
...@@ -22,7 +22,7 @@ import java.util.Random; ...@@ -22,7 +22,7 @@ import java.util.Random;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
@Command(label = "spawn", usage = "spawn <entityId> [amount] [level(monster only)]", permission = "server.spawn", description = "commands.spawn.description") @Command(label = "spawn", usage = "spawn <entityId> [amount] [level(monster only)]", permission = "server.spawn", permissionTargeted = "server.spawn.others", description = "commands.spawn.description")
public final class SpawnCommand implements CommandHandler { public final class SpawnCommand implements CommandHandler {
@Override @Override
...@@ -46,13 +46,13 @@ public final class SpawnCommand implements CommandHandler { ...@@ -46,13 +46,13 @@ public final class SpawnCommand implements CommandHandler {
try { try {
amount = Integer.parseInt(args.get(1)); amount = Integer.parseInt(args.get(1));
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, translate("commands.generic.error.amount")); CommandHandler.sendMessage(sender, translate("commands.generic.invalid.amount"));
} // Fallthrough } // Fallthrough
case 1: case 1:
try { try {
id = Integer.parseInt(args.get(0)); id = Integer.parseInt(args.get(0));
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, translate("commands.generic.error.entityId")); CommandHandler.sendMessage(sender, translate("commands.generic.invalid.entityId"));
} }
break; break;
default: default:
...@@ -64,7 +64,7 @@ public final class SpawnCommand implements CommandHandler { ...@@ -64,7 +64,7 @@ public final class SpawnCommand implements CommandHandler {
GadgetData gadgetData = GameData.getGadgetDataMap().get(id); GadgetData gadgetData = GameData.getGadgetDataMap().get(id);
ItemData itemData = GameData.getItemDataMap().get(id); ItemData itemData = GameData.getItemDataMap().get(id);
if (monsterData == null && gadgetData == null && itemData == null) { if (monsterData == null && gadgetData == null && itemData == null) {
CommandHandler.sendMessage(sender, translate("commands.generic.error.entityId")); CommandHandler.sendMessage(sender, translate("commands.generic.invalid.entityId"));
return; return;
} }
Scene scene = targetPlayer.getScene(); Scene scene = targetPlayer.getScene();
......
...@@ -14,7 +14,7 @@ import java.util.List; ...@@ -14,7 +14,7 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
@Command(label = "talent", usage = "talent <talentID> <value>", permission = "player.settalent", description = "commands.talent.description") @Command(label = "talent", usage = "talent <talentID> <value>", permission = "player.settalent", permissionTargeted = "player.settalent.others", description = "commands.talent.description")
public final class TalentCommand implements CommandHandler { public final class TalentCommand implements CommandHandler {
private void setTalentLevel(Player sender, Player player, Avatar avatar, int talentId, int talentLevel) { private void setTalentLevel(Player sender, Player player, Avatar avatar, int talentId, int talentLevel) {
int oldLevel = avatar.getSkillLevelMap().get(talentId); int oldLevel = avatar.getSkillLevelMap().get(talentId);
...@@ -24,7 +24,7 @@ public final class TalentCommand implements CommandHandler { ...@@ -24,7 +24,7 @@ public final class TalentCommand implements CommandHandler {
} }
// Upgrade skill // Upgrade skill
avatar.getSkillLevelMap().put(talentLevel, talentLevel); avatar.getSkillLevelMap().put(talentId, talentLevel);
avatar.save(); avatar.save();
// Packet // Packet
......
...@@ -10,7 +10,7 @@ import java.util.List; ...@@ -10,7 +10,7 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
@Command(label = "tpall", usage = "tpall", permission = "player.tpall", description = "commands.teleportAll.description") @Command(label = "tpall", usage = "tpall", permission = "player.tpall", permissionTargeted = "player.tpall.others", description = "commands.teleportAll.description")
public final class TeleportAllCommand implements CommandHandler { public final class TeleportAllCommand implements CommandHandler {
@Override @Override
......
...@@ -10,7 +10,7 @@ import java.util.List; ...@@ -10,7 +10,7 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
@Command(label = "teleport", usage = "teleport <x> <y> <z> [scene id]", aliases = {"tp"}, permission = "player.teleport", description = "commands.teleport.description") @Command(label = "teleport", usage = "teleport <x> <y> <z> [scene id]", aliases = {"tp"}, permission = "player.teleport", permissionTargeted = "player.teleport.others", description = "commands.teleport.description")
public final class TeleportCommand implements CommandHandler { public final class TeleportCommand implements CommandHandler {
private float parseRelative(String input, Float current) { // TODO: Maybe this will be useful elsewhere later private float parseRelative(String input, Float current) { // TODO: Maybe this will be useful elsewhere later
......
...@@ -10,7 +10,7 @@ import java.util.List; ...@@ -10,7 +10,7 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
@Command(label = "unlocktower", usage = "unlocktower", aliases = {"ut"}, @Command(label = "unlocktower", usage = "unlocktower", aliases = {"ut"},
description = "Unlock all levels of tower", permission = "player.tower") description = "commands.unlocktower.description", permission = "player.tower")
public class UnlockTowerCommand implements CommandHandler { public class UnlockTowerCommand implements CommandHandler {
@Override @Override
...@@ -21,7 +21,7 @@ public class UnlockTowerCommand implements CommandHandler { ...@@ -21,7 +21,7 @@ public class UnlockTowerCommand implements CommandHandler {
unlockFloor(sender, sender.getServer().getTowerScheduleManager() unlockFloor(sender, sender.getServer().getTowerScheduleManager()
.getScheduleFloors()); .getScheduleFloors());
CommandHandler.sendMessage(sender, translate("commands.tower.unlock_done")); CommandHandler.sendMessage(sender, translate("commands.unlocktower.success"));
} }
public void unlockFloor(Player player, List<Integer> floors){ public void unlockFloor(Player player, List<Integer> floors){
......
...@@ -11,7 +11,7 @@ import java.util.List; ...@@ -11,7 +11,7 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
@Command(label = "weather", usage = "weather <weatherId> [climateId]", aliases = {"w"}, permission = "player.weather", description = "commands.weather.description") @Command(label = "weather", usage = "weather <weatherId> [climateId]", aliases = {"w"}, permission = "player.weather", permissionTargeted = "player.weather.others", description = "commands.weather.description")
public final class WeatherCommand implements CommandHandler { public final class WeatherCommand implements CommandHandler {
@Override @Override
......
package emu.grasscutter.game; package emu.grasscutter.game;
import dev.morphia.annotations.*; import dev.morphia.annotations.*;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.utils.Crypto; import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
...@@ -107,11 +108,41 @@ public class Account { ...@@ -107,11 +108,41 @@ public class Account {
this.permissions.add(permission); return true; this.permissions.add(permission); return true;
} }
public static boolean permissionMatchesWildcard(String wildcard, String[] permissionParts) {
String[] wildcardParts = wildcard.split("\\.");
if (permissionParts.length < wildcardParts.length) { // A longer wildcard can never match a shorter permission
return false;
}
for (int i=0; i<wildcardParts.length; i++) {
switch (wildcardParts[i]) {
case "**": // Recursing match
return true;
case "*": // Match only one layer
if (i >= (permissionParts.length-1)) {
return true;
}
break;
default: // This layer isn't a wildcard, it needs to match exactly
if (!wildcardParts[i].equals(permissionParts[i])) {
return false;
}
}
}
// At this point the wildcard will have matched every layer, but if it is shorter then the permission then this is not a match at this point (no **).
return (wildcardParts.length == permissionParts.length);
}
public boolean hasPermission(String permission) { public boolean hasPermission(String permission) {
return this.permissions.contains(permission) || if (this.permissions.contains(permission) || this.permissions.contains("*")) {
this.permissions.contains("*") || return true;
(this.permissions.contains("player") || this.permissions.contains("player.*")) && permission.startsWith("player.") || }
(this.permissions.contains("server") || this.permissions.contains("server.*")) && permission.startsWith("server."); String[] permissionParts = permission.split("\\.");
for (String p : this.permissions) {
if (permissionMatchesWildcard(p, permissionParts)) {
return true;
}
}
return false;
} }
public boolean removePermission(String permission) { public boolean removePermission(String permission) {
......
...@@ -9,6 +9,10 @@ public class TowerDungeonSettleListener implements DungeonSettleListener { ...@@ -9,6 +9,10 @@ public class TowerDungeonSettleListener implements DungeonSettleListener {
@Override @Override
public void onDungeonSettle(Scene scene) { public void onDungeonSettle(Scene scene) {
if(scene.getScriptManager().getVariables().containsKey("stage")
&& scene.getScriptManager().getVariables().get("stage") == 1){
return;
}
scene.setAutoCloseTime(Utils.getCurrentSeconds() + 1000); scene.setAutoCloseTime(Utils.getCurrentSeconds() + 1000);
var towerManager = scene.getPlayers().get(0).getTowerManager(); var towerManager = scene.getPlayers().get(0).getTowerManager();
......
...@@ -117,7 +117,9 @@ public class EntityMonster extends GameEntity { ...@@ -117,7 +117,9 @@ public class EntityMonster extends GameEntity {
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry()); this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
} }
if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) { if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) {
getScene().getScriptManager().onMonsterDie(); if(getScene().getScriptManager().getScriptMonsterSpawnService() != null){
getScene().getScriptManager().getScriptMonsterSpawnService().onMonsterDead(this);
}
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, null); getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, null);
} }
if (getScene().getChallenge() != null && getScene().getChallenge().getGroup().id == this.getGroupId()) { if (getScene().getChallenge() != null && getScene().getChallenge().getGroup().id == this.getGroupId()) {
......
...@@ -190,14 +190,17 @@ public class StaminaManager { ...@@ -190,14 +190,17 @@ public class StaminaManager {
// Returns new stamina and sends PlayerPropNotify // Returns new stamina and sends PlayerPropNotify
public int setStamina(GameSession session, String reason, int newStamina) { public int setStamina(GameSession session, String reason, int newStamina) {
// set stamina if (Grasscutter.getConfig().OpenStamina) {
player.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina); // set stamina
session.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA)); player.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina);
// notify updated session.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA));
for (Map.Entry<String, AfterUpdateStaminaListener> listener : afterUpdateStaminaListeners.entrySet()) { // notify updated
listener.getValue().onAfterUpdateStamina(reason, newStamina); for (Map.Entry<String, AfterUpdateStaminaListener> listener : afterUpdateStaminaListeners.entrySet()) {
listener.getValue().onAfterUpdateStamina(reason, newStamina);
}
return newStamina;
} }
return newStamina; return player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
} }
// Kills avatar, removes entity and sends notification. // Kills avatar, removes entity and sends notification.
...@@ -243,15 +246,16 @@ public class StaminaManager { ...@@ -243,15 +246,16 @@ public class StaminaManager {
cachedEntity = entity; cachedEntity = entity;
MotionInfo motionInfo = moveInfo.getMotionInfo(); MotionInfo motionInfo = moveInfo.getMotionInfo();
MotionState motionState = motionInfo.getState(); MotionState motionState = motionInfo.getState();
boolean isReliable = moveInfo.getIsReliable(); int notifyEntityId = entity.getId();
Grasscutter.getLogger().trace("" + motionState + "\t" + (isReliable ? "reliable" : "")); int currentAvatarEntityId = session.getPlayer().getTeamManager().getCurrentAvatarEntity().getId();
if (isReliable) { if (notifyEntityId != currentAvatarEntityId) {
currentState = motionState; return;
Vector posVector = motionInfo.getPos(); }
Position newPos = new Position(posVector.getX(), posVector.getY(), posVector.getZ()); currentState = motionState;
if (newPos.getX() != 0 && newPos.getY() != 0 && newPos.getZ() != 0) { Vector posVector = motionInfo.getPos();
currentCoordinates = newPos; Position newPos = new Position(posVector.getX(), posVector.getY(), posVector.getZ());
} if (newPos.getX() != 0 && newPos.getY() != 0 && newPos.getZ() != 0) {
currentCoordinates = newPos;
} }
startSustainedStaminaHandler(); startSustainedStaminaHandler();
handleImmediateStamina(session, motionInfo, motionState, entity); handleImmediateStamina(session, motionInfo, motionState, entity);
...@@ -287,50 +291,48 @@ public class StaminaManager { ...@@ -287,50 +291,48 @@ public class StaminaManager {
private class SustainedStaminaHandler extends TimerTask { private class SustainedStaminaHandler extends TimerTask {
public void run() { public void run() {
if (Grasscutter.getConfig().OpenStamina) { boolean moving = isPlayerMoving();
boolean moving = isPlayerMoving(); int currentStamina = player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
int currentStamina = player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA); int maxStamina = player.getProperty(PlayerProperty.PROP_MAX_STAMINA);
int maxStamina = player.getProperty(PlayerProperty.PROP_MAX_STAMINA); if (moving || (currentStamina < maxStamina)) {
if (moving || (currentStamina < maxStamina)) { Grasscutter.getLogger().trace("Player moving: " + moving + ", stamina full: " +
Grasscutter.getLogger().trace("Player moving: " + moving + ", stamina full: " + (currentStamina >= maxStamina) + ", recalculate stamina");
(currentStamina >= maxStamina) + ", recalculate stamina");
Consumption consumption = new Consumption(ConsumptionType.None);
Consumption consumption = new Consumption(ConsumptionType.None); if (MotionStatesCategorized.get("CLIMB").contains(currentState)) {
if (MotionStatesCategorized.get("CLIMB").contains(currentState)) { consumption = getClimbSustainedConsumption();
consumption = getClimbSustainedConsumption(); } else if (MotionStatesCategorized.get("SWIM").contains((currentState))) {
} else if (MotionStatesCategorized.get("SWIM").contains((currentState))) { consumption = getSwimSustainedConsumptions();
consumption = getSwimSustainedConsumptions(); } else if (MotionStatesCategorized.get("RUN").contains(currentState)) {
} else if (MotionStatesCategorized.get("RUN").contains(currentState)) { consumption = getRunWalkDashSustainedConsumption();
consumption = getRunWalkDashSustainedConsumption(); } else if (MotionStatesCategorized.get("FLY").contains(currentState)) {
} else if (MotionStatesCategorized.get("FLY").contains(currentState)) { consumption = getFlySustainedConsumption();
consumption = getFlySustainedConsumption(); } else if (MotionStatesCategorized.get("STANDBY").contains(currentState)) {
} else if (MotionStatesCategorized.get("STANDBY").contains(currentState)) { consumption = getStandSustainedConsumption();
consumption = getStandSustainedConsumption(); }
}
/* /*
TODO: Reductions that apply to all motion types: TODO: Reductions that apply to all motion types:
Elemental Resonance Elemental Resonance
Wind: -15% Wind: -15%
Skills Skills
Diona E: -10% while shield lasts Diona E: -10% while shield lasts
Barbara E: -12% while lasts Barbara E: -12% while lasts
*/ */
if (cachedSession != null) { if (cachedSession != null) {
if (consumption.amount < 0) { if (consumption.amount < 0) {
staminaRecoverDelay = 0; staminaRecoverDelay = 0;
} }
if (consumption.amount > 0 && consumption.consumptionType != ConsumptionType.POWERED_FLY) { if (consumption.amount > 0 && consumption.consumptionType != ConsumptionType.POWERED_FLY) {
// For POWERED_FLY recover immediately - things like Amber's gliding exam may require this. // For POWERED_FLY recover immediately - things like Amber's gliding exam may require this.
if (staminaRecoverDelay < 10) { if (staminaRecoverDelay < 10) {
// For others recover after 2 seconds (10 ticks) - as official server does. // For others recover after 2 seconds (10 ticks) - as official server does.
staminaRecoverDelay++; staminaRecoverDelay++;
consumption.amount = 0; consumption.amount = 0;
Grasscutter.getLogger().trace("[StaminaManager] Delaying recovery: " + staminaRecoverDelay); Grasscutter.getLogger().trace("[StaminaManager] Delaying recovery: " + staminaRecoverDelay);
}
} }
updateStaminaRelative(cachedSession, consumption);
} }
updateStaminaRelative(cachedSession, consumption);
} }
} }
previousState = currentState; previousState = currentState;
......
...@@ -7,10 +7,7 @@ import emu.grasscutter.data.def.TowerLevelData; ...@@ -7,10 +7,7 @@ import emu.grasscutter.data.def.TowerLevelData;
import emu.grasscutter.game.dungeons.DungeonSettleListener; import emu.grasscutter.game.dungeons.DungeonSettleListener;
import emu.grasscutter.game.dungeons.TowerDungeonSettleListener; import emu.grasscutter.game.dungeons.TowerDungeonSettleListener;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.packet.send.PacketCanUseSkillNotify; import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.server.packet.send.PacketTowerCurLevelRecordChangeNotify;
import emu.grasscutter.server.packet.send.PacketTowerEnterLevelRsp;
import emu.grasscutter.server.packet.send.PacketTowerLevelStarCondNotify;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
...@@ -152,4 +149,10 @@ public class TowerManager { ...@@ -152,4 +149,10 @@ public class TowerManager {
return recordMap.get(player.getServer().getTowerScheduleManager().getLastEntranceFloor()) return recordMap.get(player.getServer().getTowerScheduleManager().getLastEntranceFloor())
.getStarCount() >= 6; .getStarCount() >= 6;
} }
public void mirrorTeamSetUp(int teamId) {
// use team user choose
player.getTeamManager().useTemporaryTeam(teamId);
player.sendPacket(new PacketTowerMiddleLevelChangeTeamNotify());
}
} }
...@@ -49,6 +49,7 @@ public class TowerScheduleManager { ...@@ -49,6 +49,7 @@ public class TowerScheduleManager {
public int getNextFloorId(int floorId){ public int getNextFloorId(int floorId){
var entranceFloors = getCurrentTowerScheduleData().getEntranceFloorId(); var entranceFloors = getCurrentTowerScheduleData().getEntranceFloorId();
var scheduleFloors = getScheduleFloors();
var nextId = 0; var nextId = 0;
// find in entrance floors first // find in entrance floors first
for(int i=0;i<entranceFloors.size()-1;i++){ for(int i=0;i<entranceFloors.size()-1;i++){
...@@ -56,10 +57,12 @@ public class TowerScheduleManager { ...@@ -56,10 +57,12 @@ public class TowerScheduleManager {
nextId = entranceFloors.get(i+1); nextId = entranceFloors.get(i+1);
} }
} }
if(floorId == entranceFloors.get(entranceFloors.size()-1)){
nextId = scheduleFloors.get(0);
}
if(nextId != 0){ if(nextId != 0){
return nextId; return nextId;
} }
var scheduleFloors = getScheduleFloors();
// find in schedule floors // find in schedule floors
for(int i=0;i<scheduleFloors.size()-1;i++){ for(int i=0;i<scheduleFloors.size()-1;i++){
if(floorId == scheduleFloors.get(i)){ if(floorId == scheduleFloors.get(i)){
......
package emu.grasscutter.scripts; package emu.grasscutter.scripts;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.script.Bindings; import javax.script.Bindings;
import javax.script.CompiledScript; import javax.script.CompiledScript;
import javax.script.ScriptException; import javax.script.ScriptException;
import emu.grasscutter.scripts.service.ScriptMonsterSpawnService;
import emu.grasscutter.scripts.service.ScriptMonsterTideService;
import org.luaj.vm2.LuaError;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.jse.CoerceJavaToLua; import org.luaj.vm2.lib.jse.CoerceJavaToLua;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.MonsterData;
import emu.grasscutter.data.def.WorldLevelData;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneBlock; import emu.grasscutter.scripts.data.SceneBlock;
...@@ -39,28 +36,36 @@ public class SceneScriptManager { ...@@ -39,28 +36,36 @@ public class SceneScriptManager {
private final ScriptLib scriptLib; private final ScriptLib scriptLib;
private final LuaValue scriptLibLua; private final LuaValue scriptLibLua;
private final Map<String, Integer> variables; private final Map<String, Integer> variables;
private Bindings bindings; private Bindings bindings;
private SceneConfig config; private SceneConfig config;
private List<SceneBlock> blocks; private List<SceneBlock> blocks;
private boolean isInit; private boolean isInit;
/**
private final Int2ObjectOpenHashMap<Set<SceneTrigger>> triggers; * SceneTrigger Set
*/
private final Map<String, SceneTrigger> triggers;
/**
* current triggers controlled by RefreshGroup
*/
private final Int2ObjectOpenHashMap<Set<SceneTrigger>> currentTriggers;
private final Int2ObjectOpenHashMap<SceneRegion> regions; private final Int2ObjectOpenHashMap<SceneRegion> regions;
private Map<Integer,SceneGroup> sceneGroups;
private SceneGroup currentGroup; private SceneGroup currentGroup;
private AtomicInteger monsterAlive; private ScriptMonsterTideService scriptMonsterTideService;
private AtomicInteger monsterTideCount; private ScriptMonsterSpawnService scriptMonsterSpawnService;
private int monsterSceneLimit;
private ConcurrentLinkedQueue<Integer> monsterOrders;
public SceneScriptManager(Scene scene) { public SceneScriptManager(Scene scene) {
this.scene = scene; this.scene = scene;
this.scriptLib = new ScriptLib(this); this.scriptLib = new ScriptLib(this);
this.scriptLibLua = CoerceJavaToLua.coerce(this.scriptLib); this.scriptLibLua = CoerceJavaToLua.coerce(this.scriptLib);
this.triggers = new Int2ObjectOpenHashMap<>(); this.triggers = new HashMap<>();
this.currentTriggers = new Int2ObjectOpenHashMap<>();
this.regions = new Int2ObjectOpenHashMap<>(); this.regions = new Int2ObjectOpenHashMap<>();
this.variables = new HashMap<>(); this.variables = new HashMap<>();
this.sceneGroups = new HashMap<>();
this.scriptMonsterSpawnService = new ScriptMonsterSpawnService(this);
// TEMPORARY // TEMPORARY
if (this.getScene().getId() < 10) { if (this.getScene().getId() < 10) {
return; return;
...@@ -103,17 +108,35 @@ public class SceneScriptManager { ...@@ -103,17 +108,35 @@ public class SceneScriptManager {
} }
public Set<SceneTrigger> getTriggersByEvent(int eventId) { public Set<SceneTrigger> getTriggersByEvent(int eventId) {
return triggers.computeIfAbsent(eventId, e -> new HashSet<>()); return currentTriggers.computeIfAbsent(eventId, e -> new HashSet<>());
} }
public void registerTrigger(SceneTrigger trigger) { public void registerTrigger(SceneTrigger trigger) {
this.triggers.put(trigger.name, trigger);
getTriggersByEvent(trigger.event).add(trigger); getTriggersByEvent(trigger.event).add(trigger);
} }
public void deregisterTrigger(SceneTrigger trigger) { public void deregisterTrigger(SceneTrigger trigger) {
this.triggers.remove(trigger.name);
getTriggersByEvent(trigger.event).remove(trigger); getTriggersByEvent(trigger.event).remove(trigger);
} }
public void resetTriggers(List<String> triggerNames) {
for(var name : triggerNames){
var instance = triggers.get(name);
this.currentTriggers.get(instance.event).clear();
this.currentTriggers.get(instance.event).add(instance);
}
}
public void refreshGroup(SceneGroup group, int suiteIndex){
var suite = group.getSuiteByIndex(suiteIndex);
if(suite == null){
return;
}
if(suite.triggers.size() > 0){
resetTriggers(suite.triggers);
}
spawnMonstersInGroup(group, suite);
spawnGadgetsInGroup(group, suite);
}
public SceneRegion getRegionById(int id) { public SceneRegion getRegionById(int id) {
return regions.get(id); return regions.get(id);
} }
...@@ -263,6 +286,7 @@ public class SceneScriptManager { ...@@ -263,6 +286,7 @@ public class SceneScriptManager {
} }
} }
} }
this.sceneGroups.put(group.id, group);
} catch (ScriptException e) { } catch (ScriptException e) {
Grasscutter.getLogger().error("Error loading group " + group.id + " in scene " + getScene().getId(), e); Grasscutter.getLogger().error("Error loading group " + group.id + " in scene " + getScene().getId(), e);
} }
...@@ -322,96 +346,36 @@ public class SceneScriptManager { ...@@ -322,96 +346,36 @@ public class SceneScriptManager {
this.callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(entity.getConfigId())); this.callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(entity.getConfigId()));
} }
} }
public void spawnMonstersInGroup(SceneGroup group, int suiteIndex) { public void spawnMonstersInGroup(SceneGroup group, int suiteIndex) {
var suite = group.getSuiteByIndex(suiteIndex); var suite = group.getSuiteByIndex(suiteIndex);
if(suite == null){ if(suite == null){
return; return;
} }
if(suite.sceneMonsters.size() > 0){ spawnMonstersInGroup(group, suite);
this.currentGroup = group; }
this.monsterSceneLimit = 0; public void spawnMonstersInGroup(SceneGroup group, SceneSuite suite) {
suite.sceneMonsters.forEach(mob -> spawnMonstersInGroup(group, mob)); if(suite == null || suite.sceneMonsters.size() <= 0){
return;
} }
this.currentGroup = group;
suite.sceneMonsters.forEach(mob -> this.scriptMonsterSpawnService.spawnMonster(group.id, mob));
} }
public void spawnMonstersInGroup(SceneGroup group) { public void spawnMonstersInGroup(SceneGroup group) {
this.currentGroup = group; this.currentGroup = group;
this.monsterSceneLimit = 0; group.monsters.values().forEach(mob -> this.scriptMonsterSpawnService.spawnMonster(group.id, mob));
group.monsters.values().forEach(mob -> spawnMonstersInGroup(group, mob));
} }
public void spawnMonstersInGroup(SceneGroup group,Integer[] ordersConfigId, int tideCount, int sceneLimit) {
this.currentGroup = group;
this.monsterSceneLimit = sceneLimit;
this.monsterTideCount = new AtomicInteger(tideCount);
this.monsterAlive = new AtomicInteger(0);
this.monsterOrders = new ConcurrentLinkedQueue<>(List.of(ordersConfigId));
// add the last turn
group.monsters.keySet().stream()
.filter(i -> !this.monsterOrders.contains(i))
.forEach(this.monsterOrders::add);
for (int i = 0; i < sceneLimit; i++) {
spawnMonstersInGroup(group, group.monsters.get(this.monsterOrders.poll()));
}
}
public void spawnMonstersInGroup(SceneGroup group, SceneMonster monster) {
if(monster == null){
return;
}
if(this.monsterSceneLimit > 0){
this.monsterTideCount.decrementAndGet();
this.monsterAlive.incrementAndGet();
}
MonsterData data = GameData.getMonsterDataMap().get(monster.monster_id);
if (data == null) {
return;
}
// 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) { public void startMonsterTideInGroup(SceneGroup group, Integer[] ordersConfigId, int tideCount, int sceneLimit) {
level = worldLevelData.getMonsterLevel(); this.currentGroup = group;
} this.scriptMonsterTideService =
} new ScriptMonsterTideService(this, group, tideCount, sceneLimit, ordersConfigId);
// Spawn mob
EntityMonster entity = new EntityMonster(getScene(), data, monster.pos, level);
entity.getRotation().set(monster.rot);
entity.setGroupId(group.id);
entity.setConfigId(monster.config_id);
getScene().addEntity(entity);
callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(entity.getConfigId()));
} }
public void spawnMonstersByConfigId(int configId, int delayTime) {
public void onMonsterDie(){ // TODO delay
if(this.monsterSceneLimit <= 0){ this.scriptMonsterSpawnService.spawnMonster(this.currentGroup.id, this.currentGroup.monsters.get(configId));
return;
}
if(this.monsterAlive.decrementAndGet() >= this.monsterSceneLimit) {
// maybe not happen
return;
}
if(this.monsterTideCount.get() > 0){
// add more
spawnMonstersInGroup(this.currentGroup, this.currentGroup.monsters.get(this.monsterOrders.poll()));
}else if(this.monsterAlive.get() == 0){
// spawn the last turn of monsters
//callEvent(EventType.EVENT_MONSTER_TIDE_DIE, new ScriptArgs());
while(!this.monsterOrders.isEmpty()){
spawnMonstersInGroup(this.currentGroup, this.currentGroup.monsters.get(this.monsterOrders.poll()));
}
}
} }
// Events // Events
...@@ -432,17 +396,35 @@ public class SceneScriptManager { ...@@ -432,17 +396,35 @@ public class SceneScriptManager {
args = CoerceJavaToLua.coerce(params); args = CoerceJavaToLua.coerce(params);
} }
ret = condition.call(this.getScriptLibLua(), args); ret = safetyCall(trigger.condition, condition, args);
} }
if (ret.checkboolean() == true) { if (ret.isboolean() && ret.checkboolean()) {
LuaValue action = (LuaValue) this.getBindings().get(trigger.action); LuaValue action = (LuaValue) this.getBindings().get(trigger.action);
action.call(this.getScriptLibLua(), LuaValue.NIL); var arg = new ScriptArgs();
arg.param2 = 100;
var args = CoerceJavaToLua.coerce(arg);
safetyCall(trigger.action, action, args);
} }
//TODO some ret may not bool
} }
} }
// public LuaValue safetyCall(){ public LuaValue safetyCall(String name, LuaValue func, LuaValue args){
// try{
// } return func.call(this.getScriptLibLua(), args);
}catch (LuaError error){
ScriptLib.logger.error("[LUA] call trigger failed {},{}",name,args,error);
return LuaValue.valueOf(-1);
}
}
public ScriptMonsterTideService getScriptMonsterTideService() {
return scriptMonsterTideService;
}
public ScriptMonsterSpawnService getScriptMonsterSpawnService() {
return scriptMonsterSpawnService;
}
} }
...@@ -28,7 +28,17 @@ public class ScriptLib { ...@@ -28,7 +28,17 @@ public class ScriptLib {
public SceneScriptManager getSceneScriptManager() { public SceneScriptManager getSceneScriptManager() {
return sceneScriptManager; return sceneScriptManager;
} }
private String printTable(LuaTable table){
StringBuilder sb = new StringBuilder();
sb.append("{");
for(var meta : table.keys()){
sb.append(meta).append(":").append(table.get(meta)).append(",");
}
sb.append("}");
return sb.toString();
}
public int SetGadgetStateByConfigId(int configId, int gadgetState) { public int SetGadgetStateByConfigId(int configId, int gadgetState) {
logger.debug("[LUA] Call SetGadgetStateByConfigId with {},{}", logger.debug("[LUA] Call SetGadgetStateByConfigId with {},{}",
configId,gadgetState); configId,gadgetState);
...@@ -123,7 +133,7 @@ public class ScriptLib { ...@@ -123,7 +133,7 @@ public class ScriptLib {
return 1; return 1;
} }
this.getSceneScriptManager().spawnMonstersInGroup(group, ordersConfigId, tideCount, sceneLimit); this.getSceneScriptManager().startMonsterTideInGroup(group, ordersConfigId, tideCount, sceneLimit);
return 0; return 0;
} }
...@@ -204,10 +214,13 @@ public class ScriptLib { ...@@ -204,10 +214,13 @@ public class ScriptLib {
getSceneScriptManager().getVariables().put(var, getSceneScriptManager().getVariables().get(var) + value); getSceneScriptManager().getVariables().put(var, getSceneScriptManager().getVariables().get(var) + value);
return LuaValue.ZERO; return LuaValue.ZERO;
} }
/**
* Set the actions and triggers to designated group
*/
public int RefreshGroup(LuaTable table) { public int RefreshGroup(LuaTable table) {
logger.debug("[LUA] Call RefreshGroup with {}", logger.debug("[LUA] Call RefreshGroup with {}",
table); printTable(table));
// Kill and Respawn? // Kill and Respawn?
int groupId = table.get("group_id").toint(); int groupId = table.get("group_id").toint();
int suite = table.get("suite").toint(); int suite = table.get("suite").toint();
...@@ -218,8 +231,7 @@ public class ScriptLib { ...@@ -218,8 +231,7 @@ public class ScriptLib {
return 1; return 1;
} }
this.getSceneScriptManager().spawnMonstersInGroup(group, suite); getSceneScriptManager().refreshGroup(group, suite);
this.getSceneScriptManager().spawnGadgetsInGroup(group, suite);
return 0; return 0;
} }
...@@ -260,7 +272,7 @@ public class ScriptLib { ...@@ -260,7 +272,7 @@ public class ScriptLib {
public int SetMonsterBattleByGroup(int var1, int var2, int var3){ public int SetMonsterBattleByGroup(int var1, int var2, int var3){
logger.debug("[LUA] Call SetMonsterBattleByGroup with {},{},{}", logger.debug("[LUA] Call SetMonsterBattleByGroup with {},{},{}",
var1,var2,var3); var1,var2,var3);
// TODO
return 0; return 0;
} }
...@@ -270,7 +282,7 @@ public class ScriptLib { ...@@ -270,7 +282,7 @@ public class ScriptLib {
return 0; return 0;
} }
// 8-1
public int GetGroupVariableValueByGroup(String name, int groupId){ public int GetGroupVariableValueByGroup(String name, int groupId){
logger.debug("[LUA] Call GetGroupVariableValueByGroup with {},{}", logger.debug("[LUA] Call GetGroupVariableValueByGroup with {},{}",
name,groupId); name,groupId);
...@@ -288,7 +300,7 @@ public class ScriptLib { ...@@ -288,7 +300,7 @@ public class ScriptLib {
public int KillEntityByConfigId(LuaTable table){ public int KillEntityByConfigId(LuaTable table){
logger.debug("[LUA] Call KillEntityByConfigId with {}", logger.debug("[LUA] Call KillEntityByConfigId with {}",
table); printTable(table));
var configId = table.get("config_id"); var configId = table.get("config_id");
if(configId == LuaValue.NIL){ if(configId == LuaValue.NIL){
return 1; return 1;
...@@ -306,6 +318,26 @@ public class ScriptLib { ...@@ -306,6 +318,26 @@ public class ScriptLib {
logger.debug("[LUA] Call SetGroupVariableValueByGroup with {},{},{}", logger.debug("[LUA] Call SetGroupVariableValueByGroup with {},{},{}",
key,value,groupId); key,value,groupId);
getSceneScriptManager().getVariables().put(key, value);
return 0;
}
public int CreateMonster(LuaTable table){
logger.debug("[LUA] Call CreateMonster with {}",
printTable(table));
var configId = table.get("config_id").toint();
var delayTime = table.get("delay_time").toint();
getSceneScriptManager().spawnMonstersByConfigId(configId, delayTime);
return 0;
}
public int TowerMirrorTeamSetUp(int team, int var1) {
logger.debug("[LUA] Call TowerMirrorTeamSetUp with {},{}",
team,var1);
getSceneScriptManager().getScene().getPlayers().get(0).getTowerManager().mirrorTeamSetUp(team-1);
return 0; return 0;
} }
......
...@@ -7,4 +7,18 @@ public class SceneTrigger { ...@@ -7,4 +7,18 @@ public class SceneTrigger {
public String source; public String source;
public String condition; public String condition;
public String action; public String action;
@Override
public boolean equals(Object obj) {
if(obj instanceof SceneTrigger sceneTrigger){
return this.name.equals(sceneTrigger.name);
}
return super.equals(obj);
}
@Override
public int hashCode() {
return name.hashCode();
}
} }
package emu.grasscutter.scripts.service;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.MonsterData;
import emu.grasscutter.data.def.WorldLevelData;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.scripts.SceneScriptManager;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneMonster;
import emu.grasscutter.scripts.data.ScriptArgs;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public class ScriptMonsterSpawnService {
private final SceneScriptManager sceneScriptManager;
private final List<Consumer<EntityMonster>> onMonsterCreatedListener = new ArrayList<>();
private final List<Consumer<EntityMonster>> onMonsterDeadListener = new ArrayList<>();
public ScriptMonsterSpawnService(SceneScriptManager sceneScriptManager){
this.sceneScriptManager = sceneScriptManager;
}
public void addMonsterCreatedListener(Consumer<EntityMonster> consumer){
onMonsterCreatedListener.add(consumer);
}
public void addMonsterDeadListener(Consumer<EntityMonster> consumer){
onMonsterDeadListener.add(consumer);
}
public void onMonsterDead(EntityMonster entityMonster){
onMonsterDeadListener.forEach(l -> l.accept(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.accept(entity));
sceneScriptManager.getScene().addEntity(entity);
sceneScriptManager.callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(entity.getConfigId()));
}
}
package emu.grasscutter.scripts.service;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.scripts.SceneScriptManager;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.SceneMonster;
import emu.grasscutter.scripts.data.ScriptArgs;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
public class ScriptMonsterTideService {
private final SceneScriptManager sceneScriptManager;
private final SceneGroup currentGroup;
private final AtomicInteger monsterAlive;
private final AtomicInteger monsterTideCount;
private final AtomicInteger monsterKillCount;
private final int monsterSceneLimit;
private final ConcurrentLinkedQueue<Integer> monsterConfigOrders;
public ScriptMonsterTideService(SceneScriptManager sceneScriptManager,
SceneGroup group, int tideCount, int monsterSceneLimit, Integer[] ordersConfigId){
this.sceneScriptManager = sceneScriptManager;
this.currentGroup = group;
this.monsterSceneLimit = monsterSceneLimit;
this.monsterTideCount = new AtomicInteger(tideCount);
this.monsterKillCount = new AtomicInteger(0);
this.monsterAlive = new AtomicInteger(0);
this.monsterConfigOrders = new ConcurrentLinkedQueue<>(List.of(ordersConfigId));
this.sceneScriptManager.getScriptMonsterSpawnService().addMonsterCreatedListener(this::onMonsterCreated);
this.sceneScriptManager.getScriptMonsterSpawnService().addMonsterDeadListener(this::onMonsterDead);
// spawn the first turn
for (int i = 0; i < this.monsterSceneLimit; i++) {
this.sceneScriptManager.getScriptMonsterSpawnService().spawnMonster(group.id, getNextMonster());
}
}
public void onMonsterCreated(EntityMonster entityMonster){
if(this.monsterSceneLimit > 0){
this.monsterTideCount.decrementAndGet();
this.monsterAlive.incrementAndGet();
}
}
public SceneMonster getNextMonster(){
var nextId = this.monsterConfigOrders.poll();
if(currentGroup.monsters.containsKey(nextId)){
return currentGroup.monsters.get(nextId);
}
// TODO some monster config_id do not exist in groups, so temporarily set it to the first
return currentGroup.monsters.values().stream().findFirst().orElse(null);
}
public void onMonsterDead(EntityMonster entityMonster){
if(this.monsterSceneLimit <= 0){
return;
}
if(this.monsterAlive.decrementAndGet() >= this.monsterSceneLimit) {
// maybe not happen
return;
}
this.monsterKillCount.incrementAndGet();
if(this.monsterTideCount.get() > 0){
// add more
this.sceneScriptManager.getScriptMonsterSpawnService().spawnMonster(this.currentGroup.id, getNextMonster());
}
// spawn the last turn of monsters
// fix the 5-2
this.sceneScriptManager.callEvent(EventType.EVENT_MONSTER_TIDE_DIE, new ScriptArgs(this.monsterKillCount.get()));
}
}
package emu.grasscutter.server.packet.send;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.TowerMiddleLevelChangeTeamNotifyOuterClass;
public class PacketTowerMiddleLevelChangeTeamNotify extends BasePacket {
public PacketTowerMiddleLevelChangeTeamNotify() {
super(PacketOpcodes.TowerMiddleLevelChangeTeamNotify);
TowerMiddleLevelChangeTeamNotifyOuterClass.TowerMiddleLevelChangeTeamNotify proto =
TowerMiddleLevelChangeTeamNotifyOuterClass.TowerMiddleLevelChangeTeamNotify.newBuilder()
.build();
this.setData(proto);
}
}
...@@ -334,6 +334,13 @@ ...@@ -334,6 +334,13 @@
}, },
"restart": { "restart": {
"description": "Restarts the current session" "description": "Restarts the current session"
},
"unlocktower": {
"success": "unlock done",
"description": "Unlock all levels of tower"
},
"resetshop": {
"description": "reset shop"
} }
} }
} }
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