mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-27 07:34:27 +00:00
commit
a2e31a24dd
3
.gitignore
vendored
3
.gitignore
vendored
@ -69,3 +69,6 @@ language/
|
||||
languages/
|
||||
gacha-mapping.js
|
||||
data/gacha_mappings.js
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
14
proto/TowerMiddleLevelChangeTeamNotify.proto
Normal file
14
proto/TowerMiddleLevelChangeTeamNotify.proto
Normal file
@ -0,0 +1,14 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
message TowerMiddleLevelChangeTeamNotify {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 2417;
|
||||
}
|
||||
|
||||
}
|
@ -150,7 +150,7 @@ public final class CommandMap {
|
||||
int uid = Integer.parseInt(targetUidStr);
|
||||
targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid);
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(player, translate("commands.generic.execution.player_exist_offline_error"));
|
||||
CommandHandler.sendMessage(player, translate("commands.execution.player_exist_offline_error"));
|
||||
} else {
|
||||
targetPlayerIds.put(playerId, uid);
|
||||
CommandHandler.sendMessage(player, translate("commands.execution.set_target", targetUidStr));
|
||||
@ -178,7 +178,7 @@ public final class CommandMap {
|
||||
int uid = Integer.parseInt(arg);
|
||||
targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid);
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(player, translate("commands.generic.execution.player_exist_offline_error"));
|
||||
CommandHandler.sendMessage(player, translate("commands.execution.player_exist_offline_error"));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -194,7 +194,7 @@ public final class CommandMap {
|
||||
if (targetPlayerIds.containsKey(playerId)) {
|
||||
targetPlayer = Grasscutter.getGameServer().getPlayerByUid(targetPlayerIds.get(playerId)); // We check every time in case the target goes offline after being targeted
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(player, translate("commands.generic.execution.player_exist_offline_error"));
|
||||
CommandHandler.sendMessage(player, translate("commands.execution.player_exist_offline_error"));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
@ -8,7 +8,7 @@ import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "changescene", usage = "changescene <scene id>", aliases = {"scene"}, permission = "player.changescene", description = "commands.changescene.description")
|
||||
@Command(label = "changescene", usage = "changescene <scene id>", aliases = {"scene"}, permission = "player.changescene", permissionTargeted = "player.changescene.others", description = "commands.changescene.description")
|
||||
public final class ChangeSceneCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
@ -31,11 +31,12 @@ public final class ChangeSceneCommand implements CommandHandler {
|
||||
}
|
||||
|
||||
boolean result = targetPlayer.getWorld().transferPlayerToScene(targetPlayer, sceneId, targetPlayer.getPos());
|
||||
CommandHandler.sendMessage(sender, translate("commands.changescene.result", Integer.toString(sceneId)));
|
||||
|
||||
if (!result) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.changescene.exists_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
CommandHandler.sendMessage(sender, translate("commands.changescene.success", Integer.toString(sceneId)));
|
||||
} catch (Exception e) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.argument_error"));
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "clear", usage = "clear <all|wp|art|mat>", //Merged /clearartifacts and /clearweapons to /clear <args> [uid]
|
||||
description = "commands.clear.description",
|
||||
aliases = {"clear"}, permission = "player.clearinv")
|
||||
aliases = {"clear"}, permission = "player.clearinv", permissionTargeted = "player.clearinv.others")
|
||||
|
||||
public final class ClearCommand implements CommandHandler {
|
||||
|
||||
|
@ -9,7 +9,7 @@ import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "coop", usage = "coop [host UID]", permission = "server.coop", description = "commands.coop.description")
|
||||
@Command(label = "coop", usage = "coop [host UID]", permission = "server.coop", permissionTargeted = "server.coop.others", description = "commands.coop.description")
|
||||
public final class CoopCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
|
@ -13,7 +13,7 @@ import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "drop", usage = "drop <itemId|itemName> [amount]", aliases = {"d", "dropitem"}, permission = "server.drop", description = "commands.drop.description")
|
||||
@Command(label = "drop", usage = "drop <itemId|itemName> [amount]", aliases = {"d", "dropitem"}, permission = "server.drop", permissionTargeted = "server.drop.others", description = "commands.drop.description")
|
||||
public final class DropCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
|
@ -8,7 +8,7 @@ import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "enterdungeon", usage = "enterdungeon <dungeon id>", aliases = {"dungeon"}, permission = "player.enterdungeon", description = "commands.enter_dungeon.description")
|
||||
@Command(label = "enterdungeon", usage = "enterdungeon <dungeon id>", aliases = {"dungeon"}, permission = "player.enterdungeon", permissionTargeted = "player.enterdungeon.others", description = "commands.enter_dungeon.description")
|
||||
public final class EnterDungeonCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
|
@ -15,7 +15,7 @@ import java.util.*;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "giveall", usage = "giveall [amount]", aliases = {"givea"}, permission = "player.giveall", threading = true, description = "commands.giveAll.description")
|
||||
@Command(label = "giveall", usage = "giveall [amount]", aliases = {"givea"}, permission = "player.giveall", permissionTargeted = "player.giveall.others", threading = true, description = "commands.giveAll.description")
|
||||
public final class GiveAllCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
|
@ -19,7 +19,7 @@ import static java.util.Map.entry;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "giveart", usage = "giveart <artifactId> <mainPropId> [<appendPropId>[,<times>]]... [level]", aliases = {"gart"}, permission = "player.giveart", description = "commands.giveArtifact.description")
|
||||
@Command(label = "giveart", usage = "giveart <artifactId> <mainPropId> [<appendPropId>[,<times>]]... [level]", aliases = {"gart"}, permission = "player.giveart", permissionTargeted = "player.giveart.others", description = "commands.giveArtifact.description")
|
||||
public final class GiveArtifactCommand implements CommandHandler {
|
||||
private static final Map<String, Map<EquipType, Integer>> mainPropMap = Map.ofEntries(
|
||||
entry("hp", Map.ofEntries(entry(EquipType.EQUIP_BRACER, 14001))),
|
||||
|
@ -12,7 +12,7 @@ import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "givechar", usage = "givechar <avatarId> [level]", aliases = {"givec"}, permission = "player.givechar", description = "commands.giveChar.description")
|
||||
@Command(label = "givechar", usage = "givechar <avatarId> [level]", aliases = {"givec"}, permission = "player.givechar", permissionTargeted = "player.givechar.others", description = "commands.giveChar.description")
|
||||
public final class GiveCharCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
|
@ -17,7 +17,7 @@ import java.util.regex.Pattern;
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "give", usage = "give <itemId|itemName> [amount] [level]", aliases = {
|
||||
"g", "item", "giveitem"}, permission = "player.give", description = "commands.give.description")
|
||||
"g", "item", "giveitem"}, permission = "player.give", permissionTargeted = "player.give.others", description = "commands.give.description")
|
||||
public final class GiveCommand implements CommandHandler {
|
||||
Pattern lvlRegex = Pattern.compile("l(?:vl?)?(\\d+)"); // Java is a joke of a proglang that doesn't have raw string literals
|
||||
Pattern refineRegex = Pattern.compile("r(\\d+)");
|
||||
|
@ -8,7 +8,7 @@ import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "godmode", usage = "godmode [on|off|toggle]", permission = "player.godmode", description = "commands.godmode.description")
|
||||
@Command(label = "godmode", usage = "godmode [on|off|toggle]", permission = "player.godmode", permissionTargeted = "player.godmode.others", description = "commands.godmode.description")
|
||||
public final class GodModeCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
|
@ -11,7 +11,7 @@ import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "heal", usage = "heal|h", aliases = {"h"}, permission = "player.heal", description = "commands.heal.description")
|
||||
@Command(label = "heal", usage = "heal|h", aliases = {"h"}, permission = "player.heal", permissionTargeted = "player.heal.others", description = "commands.heal.description")
|
||||
public final class HealCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
|
@ -12,7 +12,7 @@ import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "killall", usage = "killall [sceneId]", permission = "server.killall", description = "commands.kill.description")
|
||||
@Command(label = "killall", usage = "killall [sceneId]", permission = "server.killall", permissionTargeted = "server.killall.others", description = "commands.kill.description")
|
||||
public final class KillAllCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
|
@ -13,7 +13,7 @@ import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "killcharacter", usage = "killcharacter", aliases = {"suicide", "kill"}, permission = "player.killcharacter", description = "commands.list.description")
|
||||
@Command(label = "killcharacter", usage = "killcharacter", aliases = {"suicide", "kill"}, permission = "player.killcharacter", permissionTargeted = "player.killcharacter.others", description = "commands.list.description")
|
||||
public final class KillCharacterCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
|
@ -11,7 +11,7 @@ import java.util.List;
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "resetconst", usage = "resetconst [all]",
|
||||
aliases = {"resetconstellation"}, permission = "player.resetconstellation", description = "commands.resetConst.description")
|
||||
aliases = {"resetconstellation"}, permission = "player.resetconstellation", permissionTargeted = "player.resetconstellation.others", description = "commands.resetConst.description")
|
||||
public final class ResetConstCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
|
@ -9,7 +9,7 @@ import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "resetshop", usage = "resetshop", permission = "server.resetshop", description = "commands.status.description")
|
||||
@Command(label = "resetshop", usage = "resetshop", permission = "server.resetshop", permissionTargeted = "server.resetshop.others", description = "commands.resetshop.description")
|
||||
public final class ResetShopLimitCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
|
@ -9,7 +9,7 @@ import java.util.List;
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "say", usage = "say <message>",
|
||||
aliases = {"sendservmsg", "sendservermessage", "sendmessage"}, permission = "server.sendmessage", description = "commands.sendMessage.description")
|
||||
aliases = {"sendservmsg", "sendservermessage", "sendmessage"}, permission = "server.sendmessage", permissionTargeted = "server.sendmessage.others", description = "commands.sendMessage.description")
|
||||
public final class SendMessageCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
|
@ -12,7 +12,7 @@ import emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify;
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "setfetterlevel", usage = "setfetterlevel <level>",
|
||||
aliases = {"setfetterlvl", "setfriendship"}, permission = "player.setfetterlevel", description = "commands.setFetterLevel.description")
|
||||
aliases = {"setfetterlvl", "setfriendship"}, permission = "player.setfetterlevel", permissionTargeted = "player.setfetterlevel.others", description = "commands.setFetterLevel.description")
|
||||
public final class SetFetterLevelCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
|
@ -15,7 +15,7 @@ import emu.grasscutter.utils.Language;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "setstats", usage = "setstats|stats <stat> <value>", aliases = {"stats"}, permission = "player.setstats", description = "commands.setStats.description")
|
||||
@Command(label = "setstats", usage = "setstats|stats <stat> <value>", aliases = {"stats"}, permission = "player.setstats", permissionTargeted = "player.setstats.others", description = "commands.setStats.description")
|
||||
public final class SetStatsCommand implements CommandHandler {
|
||||
static class Stat {
|
||||
String name;
|
||||
@ -175,7 +175,7 @@ public final class SetStatsCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
String syntax = sender == null ? translate("commands.setStats.usage_console") : translate("commands.setStats.ingame");
|
||||
String syntax = sender == null ? translate("commands.setStats.usage_console") : translate("commands.setStats.usage_ingame");
|
||||
String usage = syntax + translate("commands.setStats.help_message");
|
||||
String statStr;
|
||||
String valueStr;
|
||||
|
@ -10,7 +10,7 @@ import java.util.List;
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@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 {
|
||||
|
||||
@Override
|
||||
|
@ -22,7 +22,7 @@ import java.util.Random;
|
||||
|
||||
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 {
|
||||
|
||||
@Override
|
||||
@ -46,13 +46,13 @@ public final class SpawnCommand implements CommandHandler {
|
||||
try {
|
||||
amount = Integer.parseInt(args.get(1));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.error.amount"));
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.invalid.amount"));
|
||||
} // Fallthrough
|
||||
case 1:
|
||||
try {
|
||||
id = Integer.parseInt(args.get(0));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.error.entityId"));
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.invalid.entityId"));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -64,7 +64,7 @@ public final class SpawnCommand implements CommandHandler {
|
||||
GadgetData gadgetData = GameData.getGadgetDataMap().get(id);
|
||||
ItemData itemData = GameData.getItemDataMap().get(id);
|
||||
if (monsterData == null && gadgetData == null && itemData == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.error.entityId"));
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.invalid.entityId"));
|
||||
return;
|
||||
}
|
||||
Scene scene = targetPlayer.getScene();
|
||||
|
@ -14,7 +14,7 @@ import java.util.List;
|
||||
|
||||
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 {
|
||||
private void setTalentLevel(Player sender, Player player, Avatar avatar, int talentId, int talentLevel) {
|
||||
int oldLevel = avatar.getSkillLevelMap().get(talentId);
|
||||
@ -24,7 +24,7 @@ public final class TalentCommand implements CommandHandler {
|
||||
}
|
||||
|
||||
// Upgrade skill
|
||||
avatar.getSkillLevelMap().put(talentLevel, talentLevel);
|
||||
avatar.getSkillLevelMap().put(talentId, talentLevel);
|
||||
avatar.save();
|
||||
|
||||
// Packet
|
||||
|
@ -10,7 +10,7 @@ import java.util.List;
|
||||
|
||||
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 {
|
||||
|
||||
@Override
|
||||
|
@ -10,7 +10,7 @@ import java.util.List;
|
||||
|
||||
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 {
|
||||
|
||||
private float parseRelative(String input, Float current) { // TODO: Maybe this will be useful elsewhere later
|
||||
|
@ -10,7 +10,7 @@ import java.util.List;
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@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 {
|
||||
|
||||
@Override
|
||||
@ -21,7 +21,7 @@ public class UnlockTowerCommand implements CommandHandler {
|
||||
unlockFloor(sender, sender.getServer().getTowerScheduleManager()
|
||||
.getScheduleFloors());
|
||||
|
||||
CommandHandler.sendMessage(sender, translate("commands.tower.unlock_done"));
|
||||
CommandHandler.sendMessage(sender, translate("commands.unlocktower.success"));
|
||||
}
|
||||
|
||||
public void unlockFloor(Player player, List<Integer> floors){
|
||||
|
@ -11,7 +11,7 @@ import java.util.List;
|
||||
|
||||
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 {
|
||||
|
||||
@Override
|
||||
|
@ -1,6 +1,7 @@
|
||||
package emu.grasscutter.game;
|
||||
|
||||
import dev.morphia.annotations.*;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.utils.Crypto;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
@ -107,11 +108,41 @@ public class Account {
|
||||
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) {
|
||||
return this.permissions.contains(permission) ||
|
||||
this.permissions.contains("*") ||
|
||||
(this.permissions.contains("player") || this.permissions.contains("player.*")) && permission.startsWith("player.") ||
|
||||
(this.permissions.contains("server") || this.permissions.contains("server.*")) && permission.startsWith("server.");
|
||||
if (this.permissions.contains(permission) || this.permissions.contains("*")) {
|
||||
return true;
|
||||
}
|
||||
String[] permissionParts = permission.split("\\.");
|
||||
for (String p : this.permissions) {
|
||||
if (permissionMatchesWildcard(p, permissionParts)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean removePermission(String permission) {
|
||||
|
@ -9,6 +9,10 @@ public class TowerDungeonSettleListener implements DungeonSettleListener {
|
||||
|
||||
@Override
|
||||
public void onDungeonSettle(Scene scene) {
|
||||
if(scene.getScriptManager().getVariables().containsKey("stage")
|
||||
&& scene.getScriptManager().getVariables().get("stage") == 1){
|
||||
return;
|
||||
}
|
||||
scene.setAutoCloseTime(Utils.getCurrentSeconds() + 1000);
|
||||
var towerManager = scene.getPlayers().get(0).getTowerManager();
|
||||
|
||||
|
@ -117,7 +117,9 @@ public class EntityMonster extends GameEntity {
|
||||
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
|
||||
}
|
||||
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);
|
||||
}
|
||||
if (getScene().getChallenge() != null && getScene().getChallenge().getGroup().id == this.getGroupId()) {
|
||||
|
@ -190,14 +190,17 @@ public class StaminaManager {
|
||||
|
||||
// Returns new stamina and sends PlayerPropNotify
|
||||
public int setStamina(GameSession session, String reason, int newStamina) {
|
||||
// set stamina
|
||||
player.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina);
|
||||
session.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA));
|
||||
// notify updated
|
||||
for (Map.Entry<String, AfterUpdateStaminaListener> listener : afterUpdateStaminaListeners.entrySet()) {
|
||||
listener.getValue().onAfterUpdateStamina(reason, newStamina);
|
||||
if (Grasscutter.getConfig().OpenStamina) {
|
||||
// set stamina
|
||||
player.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina);
|
||||
session.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA));
|
||||
// notify updated
|
||||
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.
|
||||
@ -243,15 +246,16 @@ public class StaminaManager {
|
||||
cachedEntity = entity;
|
||||
MotionInfo motionInfo = moveInfo.getMotionInfo();
|
||||
MotionState motionState = motionInfo.getState();
|
||||
boolean isReliable = moveInfo.getIsReliable();
|
||||
Grasscutter.getLogger().trace("" + motionState + "\t" + (isReliable ? "reliable" : ""));
|
||||
if (isReliable) {
|
||||
currentState = motionState;
|
||||
Vector posVector = motionInfo.getPos();
|
||||
Position newPos = new Position(posVector.getX(), posVector.getY(), posVector.getZ());
|
||||
if (newPos.getX() != 0 && newPos.getY() != 0 && newPos.getZ() != 0) {
|
||||
currentCoordinates = newPos;
|
||||
}
|
||||
int notifyEntityId = entity.getId();
|
||||
int currentAvatarEntityId = session.getPlayer().getTeamManager().getCurrentAvatarEntity().getId();
|
||||
if (notifyEntityId != currentAvatarEntityId) {
|
||||
return;
|
||||
}
|
||||
currentState = motionState;
|
||||
Vector posVector = motionInfo.getPos();
|
||||
Position newPos = new Position(posVector.getX(), posVector.getY(), posVector.getZ());
|
||||
if (newPos.getX() != 0 && newPos.getY() != 0 && newPos.getZ() != 0) {
|
||||
currentCoordinates = newPos;
|
||||
}
|
||||
startSustainedStaminaHandler();
|
||||
handleImmediateStamina(session, motionInfo, motionState, entity);
|
||||
@ -287,50 +291,48 @@ public class StaminaManager {
|
||||
|
||||
private class SustainedStaminaHandler extends TimerTask {
|
||||
public void run() {
|
||||
if (Grasscutter.getConfig().OpenStamina) {
|
||||
boolean moving = isPlayerMoving();
|
||||
int currentStamina = player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||
int maxStamina = player.getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
||||
if (moving || (currentStamina < maxStamina)) {
|
||||
Grasscutter.getLogger().trace("Player moving: " + moving + ", stamina full: " +
|
||||
(currentStamina >= maxStamina) + ", recalculate stamina");
|
||||
boolean moving = isPlayerMoving();
|
||||
int currentStamina = player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||
int maxStamina = player.getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
||||
if (moving || (currentStamina < maxStamina)) {
|
||||
Grasscutter.getLogger().trace("Player moving: " + moving + ", stamina full: " +
|
||||
(currentStamina >= maxStamina) + ", recalculate stamina");
|
||||
|
||||
Consumption consumption = new Consumption(ConsumptionType.None);
|
||||
if (MotionStatesCategorized.get("CLIMB").contains(currentState)) {
|
||||
consumption = getClimbSustainedConsumption();
|
||||
} else if (MotionStatesCategorized.get("SWIM").contains((currentState))) {
|
||||
consumption = getSwimSustainedConsumptions();
|
||||
} else if (MotionStatesCategorized.get("RUN").contains(currentState)) {
|
||||
consumption = getRunWalkDashSustainedConsumption();
|
||||
} else if (MotionStatesCategorized.get("FLY").contains(currentState)) {
|
||||
consumption = getFlySustainedConsumption();
|
||||
} else if (MotionStatesCategorized.get("STANDBY").contains(currentState)) {
|
||||
consumption = getStandSustainedConsumption();
|
||||
}
|
||||
Consumption consumption = new Consumption(ConsumptionType.None);
|
||||
if (MotionStatesCategorized.get("CLIMB").contains(currentState)) {
|
||||
consumption = getClimbSustainedConsumption();
|
||||
} else if (MotionStatesCategorized.get("SWIM").contains((currentState))) {
|
||||
consumption = getSwimSustainedConsumptions();
|
||||
} else if (MotionStatesCategorized.get("RUN").contains(currentState)) {
|
||||
consumption = getRunWalkDashSustainedConsumption();
|
||||
} else if (MotionStatesCategorized.get("FLY").contains(currentState)) {
|
||||
consumption = getFlySustainedConsumption();
|
||||
} else if (MotionStatesCategorized.get("STANDBY").contains(currentState)) {
|
||||
consumption = getStandSustainedConsumption();
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: Reductions that apply to all motion types:
|
||||
Elemental Resonance
|
||||
Wind: -15%
|
||||
Skills
|
||||
Diona E: -10% while shield lasts
|
||||
Barbara E: -12% while lasts
|
||||
*/
|
||||
if (cachedSession != null) {
|
||||
if (consumption.amount < 0) {
|
||||
staminaRecoverDelay = 0;
|
||||
}
|
||||
if (consumption.amount > 0 && consumption.consumptionType != ConsumptionType.POWERED_FLY) {
|
||||
// For POWERED_FLY recover immediately - things like Amber's gliding exam may require this.
|
||||
if (staminaRecoverDelay < 10) {
|
||||
// For others recover after 2 seconds (10 ticks) - as official server does.
|
||||
staminaRecoverDelay++;
|
||||
consumption.amount = 0;
|
||||
Grasscutter.getLogger().trace("[StaminaManager] Delaying recovery: " + staminaRecoverDelay);
|
||||
}
|
||||
}
|
||||
updateStaminaRelative(cachedSession, consumption);
|
||||
/*
|
||||
TODO: Reductions that apply to all motion types:
|
||||
Elemental Resonance
|
||||
Wind: -15%
|
||||
Skills
|
||||
Diona E: -10% while shield lasts
|
||||
Barbara E: -12% while lasts
|
||||
*/
|
||||
if (cachedSession != null) {
|
||||
if (consumption.amount < 0) {
|
||||
staminaRecoverDelay = 0;
|
||||
}
|
||||
if (consumption.amount > 0 && consumption.consumptionType != ConsumptionType.POWERED_FLY) {
|
||||
// For POWERED_FLY recover immediately - things like Amber's gliding exam may require this.
|
||||
if (staminaRecoverDelay < 10) {
|
||||
// For others recover after 2 seconds (10 ticks) - as official server does.
|
||||
staminaRecoverDelay++;
|
||||
consumption.amount = 0;
|
||||
Grasscutter.getLogger().trace("[StaminaManager] Delaying recovery: " + staminaRecoverDelay);
|
||||
}
|
||||
}
|
||||
updateStaminaRelative(cachedSession, consumption);
|
||||
}
|
||||
}
|
||||
previousState = currentState;
|
||||
|
@ -7,10 +7,7 @@ import emu.grasscutter.data.def.TowerLevelData;
|
||||
import emu.grasscutter.game.dungeons.DungeonSettleListener;
|
||||
import emu.grasscutter.game.dungeons.TowerDungeonSettleListener;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.packet.send.PacketCanUseSkillNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketTowerCurLevelRecordChangeNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketTowerEnterLevelRsp;
|
||||
import emu.grasscutter.server.packet.send.PacketTowerLevelStarCondNotify;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -152,4 +149,10 @@ public class TowerManager {
|
||||
return recordMap.get(player.getServer().getTowerScheduleManager().getLastEntranceFloor())
|
||||
.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 {
|
||||
|
||||
public int getNextFloorId(int floorId){
|
||||
var entranceFloors = getCurrentTowerScheduleData().getEntranceFloorId();
|
||||
var scheduleFloors = getScheduleFloors();
|
||||
var nextId = 0;
|
||||
// find in entrance floors first
|
||||
for(int i=0;i<entranceFloors.size()-1;i++){
|
||||
@ -56,10 +57,12 @@ public class TowerScheduleManager {
|
||||
nextId = entranceFloors.get(i+1);
|
||||
}
|
||||
}
|
||||
if(floorId == entranceFloors.get(entranceFloors.size()-1)){
|
||||
nextId = scheduleFloors.get(0);
|
||||
}
|
||||
if(nextId != 0){
|
||||
return nextId;
|
||||
}
|
||||
var scheduleFloors = getScheduleFloors();
|
||||
// find in schedule floors
|
||||
for(int i=0;i<scheduleFloors.size()-1;i++){
|
||||
if(floorId == scheduleFloors.get(i)){
|
||||
|
@ -1,23 +1,20 @@
|
||||
package emu.grasscutter.scripts;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.script.Bindings;
|
||||
import javax.script.CompiledScript;
|
||||
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.lib.jse.CoerceJavaToLua;
|
||||
|
||||
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.EntityMonster;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.scripts.constants.EventType;
|
||||
import emu.grasscutter.scripts.data.SceneBlock;
|
||||
@ -39,28 +36,36 @@ public class SceneScriptManager {
|
||||
private final ScriptLib scriptLib;
|
||||
private final LuaValue scriptLibLua;
|
||||
private final Map<String, Integer> variables;
|
||||
|
||||
private Bindings bindings;
|
||||
private SceneConfig config;
|
||||
private List<SceneBlock> blocks;
|
||||
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 Map<Integer,SceneGroup> sceneGroups;
|
||||
private SceneGroup currentGroup;
|
||||
private AtomicInteger monsterAlive;
|
||||
private AtomicInteger monsterTideCount;
|
||||
private int monsterSceneLimit;
|
||||
private ConcurrentLinkedQueue<Integer> monsterOrders;
|
||||
private ScriptMonsterTideService scriptMonsterTideService;
|
||||
private ScriptMonsterSpawnService scriptMonsterSpawnService;
|
||||
|
||||
public SceneScriptManager(Scene scene) {
|
||||
this.scene = scene;
|
||||
this.scriptLib = new ScriptLib(this);
|
||||
this.scriptLibLua = CoerceJavaToLua.coerce(this.scriptLib);
|
||||
this.triggers = new Int2ObjectOpenHashMap<>();
|
||||
this.triggers = new HashMap<>();
|
||||
this.currentTriggers = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
this.regions = new Int2ObjectOpenHashMap<>();
|
||||
this.variables = new HashMap<>();
|
||||
|
||||
this.sceneGroups = new HashMap<>();
|
||||
this.scriptMonsterSpawnService = new ScriptMonsterSpawnService(this);
|
||||
|
||||
// TEMPORARY
|
||||
if (this.getScene().getId() < 10) {
|
||||
return;
|
||||
@ -103,17 +108,35 @@ public class SceneScriptManager {
|
||||
}
|
||||
|
||||
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) {
|
||||
this.triggers.put(trigger.name, trigger);
|
||||
getTriggersByEvent(trigger.event).add(trigger);
|
||||
}
|
||||
|
||||
public void deregisterTrigger(SceneTrigger trigger) {
|
||||
this.triggers.remove(trigger.name);
|
||||
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) {
|
||||
return regions.get(id);
|
||||
}
|
||||
@ -263,6 +286,7 @@ public class SceneScriptManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
this.sceneGroups.put(group.id, group);
|
||||
} catch (ScriptException e) {
|
||||
Grasscutter.getLogger().error("Error loading group " + group.id + " in scene " + getScene().getId(), e);
|
||||
}
|
||||
@ -322,96 +346,36 @@ public class SceneScriptManager {
|
||||
this.callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(entity.getConfigId()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void spawnMonstersInGroup(SceneGroup group, int suiteIndex) {
|
||||
var suite = group.getSuiteByIndex(suiteIndex);
|
||||
if(suite == null){
|
||||
return;
|
||||
}
|
||||
if(suite.sceneMonsters.size() > 0){
|
||||
this.currentGroup = group;
|
||||
this.monsterSceneLimit = 0;
|
||||
suite.sceneMonsters.forEach(mob -> spawnMonstersInGroup(group, mob));
|
||||
spawnMonstersInGroup(group, suite);
|
||||
}
|
||||
public void spawnMonstersInGroup(SceneGroup group, SceneSuite suite) {
|
||||
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) {
|
||||
this.currentGroup = group;
|
||||
this.monsterSceneLimit = 0;
|
||||
group.monsters.values().forEach(mob -> spawnMonstersInGroup(group, mob));
|
||||
group.monsters.values().forEach(mob -> this.scriptMonsterSpawnService.spawnMonster(group.id, mob));
|
||||
}
|
||||
public void spawnMonstersInGroup(SceneGroup group,Integer[] ordersConfigId, int tideCount, int sceneLimit) {
|
||||
|
||||
public void startMonsterTideInGroup(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));
|
||||
this.scriptMonsterTideService =
|
||||
new ScriptMonsterTideService(this, group, tideCount, sceneLimit, 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) {
|
||||
level = worldLevelData.getMonsterLevel();
|
||||
}
|
||||
}
|
||||
|
||||
// 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 onMonsterDie(){
|
||||
if(this.monsterSceneLimit <= 0){
|
||||
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()));
|
||||
}
|
||||
}
|
||||
public void spawnMonstersByConfigId(int configId, int delayTime) {
|
||||
// TODO delay
|
||||
this.scriptMonsterSpawnService.spawnMonster(this.currentGroup.id, this.currentGroup.monsters.get(configId));
|
||||
}
|
||||
// Events
|
||||
|
||||
@ -432,17 +396,35 @@ public class SceneScriptManager {
|
||||
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);
|
||||
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 {
|
||||
public SceneScriptManager getSceneScriptManager() {
|
||||
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) {
|
||||
logger.debug("[LUA] Call SetGadgetStateByConfigId with {},{}",
|
||||
configId,gadgetState);
|
||||
@ -123,7 +133,7 @@ public class ScriptLib {
|
||||
return 1;
|
||||
}
|
||||
|
||||
this.getSceneScriptManager().spawnMonstersInGroup(group, ordersConfigId, tideCount, sceneLimit);
|
||||
this.getSceneScriptManager().startMonsterTideInGroup(group, ordersConfigId, tideCount, sceneLimit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -204,10 +214,13 @@ public class ScriptLib {
|
||||
getSceneScriptManager().getVariables().put(var, getSceneScriptManager().getVariables().get(var) + value);
|
||||
return LuaValue.ZERO;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the actions and triggers to designated group
|
||||
*/
|
||||
public int RefreshGroup(LuaTable table) {
|
||||
logger.debug("[LUA] Call RefreshGroup with {}",
|
||||
table);
|
||||
printTable(table));
|
||||
// Kill and Respawn?
|
||||
int groupId = table.get("group_id").toint();
|
||||
int suite = table.get("suite").toint();
|
||||
@ -218,8 +231,7 @@ public class ScriptLib {
|
||||
return 1;
|
||||
}
|
||||
|
||||
this.getSceneScriptManager().spawnMonstersInGroup(group, suite);
|
||||
this.getSceneScriptManager().spawnGadgetsInGroup(group, suite);
|
||||
getSceneScriptManager().refreshGroup(group, suite);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -260,7 +272,7 @@ public class ScriptLib {
|
||||
public int SetMonsterBattleByGroup(int var1, int var2, int var3){
|
||||
logger.debug("[LUA] Call SetMonsterBattleByGroup with {},{},{}",
|
||||
var1,var2,var3);
|
||||
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -270,7 +282,7 @@ public class ScriptLib {
|
||||
|
||||
return 0;
|
||||
}
|
||||
// 8-1
|
||||
|
||||
public int GetGroupVariableValueByGroup(String name, int groupId){
|
||||
logger.debug("[LUA] Call GetGroupVariableValueByGroup with {},{}",
|
||||
name,groupId);
|
||||
@ -288,7 +300,7 @@ public class ScriptLib {
|
||||
|
||||
public int KillEntityByConfigId(LuaTable table){
|
||||
logger.debug("[LUA] Call KillEntityByConfigId with {}",
|
||||
table);
|
||||
printTable(table));
|
||||
var configId = table.get("config_id");
|
||||
if(configId == LuaValue.NIL){
|
||||
return 1;
|
||||
@ -306,6 +318,26 @@ public class ScriptLib {
|
||||
logger.debug("[LUA] Call SetGroupVariableValueByGroup with {},{},{}",
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -7,4 +7,18 @@ public class SceneTrigger {
|
||||
public String source;
|
||||
public String condition;
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,73 @@
|
||||
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()));
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
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()));
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
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 @@
|
||||
},
|
||||
"restart": {
|
||||
"description": "Restarts the current session"
|
||||
},
|
||||
"unlocktower": {
|
||||
"success": "unlock done",
|
||||
"description": "Unlock all levels of tower"
|
||||
},
|
||||
"resetshop": {
|
||||
"description": "reset shop"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -184,7 +184,7 @@
|
||||
"account_error": "Konto nie może zostać znalezione."
|
||||
},
|
||||
"position": {
|
||||
"success": "Koordynaty: %.3f, %.3f, %.3f\nID sceny: %d"
|
||||
"success": "Koordynaty: %s, %s, %s\nID sceny: %s"
|
||||
},
|
||||
"reload": {
|
||||
"reload_start": "Ponowne ładowanie konfiguracji.",
|
||||
@ -293,6 +293,13 @@
|
||||
"usage": "Użycie: ",
|
||||
"aliases": "Aliasy: ",
|
||||
"available_commands": "Dostępne komendy: "
|
||||
},
|
||||
"unlocktower": {
|
||||
"success": "odblokować gotowe",
|
||||
"description": "Odblokuj głęboką spiralę"
|
||||
},
|
||||
"resetshop": {
|
||||
"description": "zresetuj sklep"
|
||||
}
|
||||
}
|
||||
}
|
@ -201,7 +201,7 @@
|
||||
"description": "给予或移除指定玩家的权限。"
|
||||
},
|
||||
"position": {
|
||||
"success": "坐标:%.3f, %.3f, %.3f\n场景ID:%d",
|
||||
"success": "坐标:%s, %s, %s\n场景ID:%s",
|
||||
"description": "获取所在位置。"
|
||||
},
|
||||
"reload": {
|
||||
@ -249,7 +249,7 @@
|
||||
"setFetterLevel": {
|
||||
"usage": "用法:setfetterlevel <level>",
|
||||
"range_error": "好感度等级必须在 0 到 10 之间。",
|
||||
"fetter_set_level": "好感度已设置为 %s 级",
|
||||
"success": "好感度已设置为 %s 级",
|
||||
"level_error": "无效的好感度等级。",
|
||||
"description": "设置当前角色的好感度等级。"
|
||||
},
|
||||
@ -330,6 +330,13 @@
|
||||
},
|
||||
"restart": {
|
||||
"description": "重新启动服务器。"
|
||||
},
|
||||
"unlocktower": {
|
||||
"success": "解锁完成。",
|
||||
"description": "解锁深境螺旋的所有层"
|
||||
},
|
||||
"resetshop": {
|
||||
"description": "重置商店时间"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -184,7 +184,7 @@
|
||||
"account_error": "The account cannot be found."
|
||||
},
|
||||
"position": {
|
||||
"success": "坐標:%.3f, %.3f, %.3f\n場景ID:%d"
|
||||
"success": "坐標:%s, %s, %s\n場景ID:%s"
|
||||
},
|
||||
"reload": {
|
||||
"reload_start": "正在重新加載設定檔。",
|
||||
@ -226,7 +226,7 @@
|
||||
"setFetterLevel": {
|
||||
"usage": "用法:setfetterlevel <level>",
|
||||
"range_error": "好感度必須在 0 到 10 之間。",
|
||||
"fetter_set_level": "好感等級已設定為 %s",
|
||||
"success": "好感等級已設定為 %s",
|
||||
"level_error": "無效的好感度。"
|
||||
},
|
||||
"setStats": {
|
||||
@ -293,6 +293,13 @@
|
||||
"usage": "用法:",
|
||||
"aliases": "別名:",
|
||||
"available_commands": "可用指令:"
|
||||
},
|
||||
"unlocktower": {
|
||||
"success": "解鎖完成。",
|
||||
"description": "解鎖所有級別的深境螺旋"
|
||||
},
|
||||
"resetshop": {
|
||||
"description": "重置商店時間"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user