diff --git a/scripts/proxy.py b/scripts/proxy.py index 06fea5900..640cd55fa 100644 --- a/scripts/proxy.py +++ b/scripts/proxy.py @@ -68,7 +68,7 @@ class MlgmXyysd_Animation_Company_Proxy: ] def request(self, flow: http.HTTPFlow) -> None: - if flow.request.host in self.LIST_DOMAINS: + if flow.request.pretty_host in self.LIST_DOMAINS: if USE_SSL: flow.request.scheme = "https" else: diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index e649b537c..8c3248dd7 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -158,6 +158,8 @@ public final class Grasscutter { // Generate handbooks. Tools.createGmHandbooks(false); + // Generate gacha mappings. + Tools.generateGachaMappings(); } // Start servers. diff --git a/src/main/java/emu/grasscutter/command/CommandHelpers.java b/src/main/java/emu/grasscutter/command/CommandHelpers.java index e43c2212c..9f09c785f 100644 --- a/src/main/java/emu/grasscutter/command/CommandHelpers.java +++ b/src/main/java/emu/grasscutter/command/CommandHelpers.java @@ -1,5 +1,6 @@ package emu.grasscutter.command; +import emu.grasscutter.game.world.Position; import java.util.*; import java.util.function.BiConsumer; import java.util.regex.*; @@ -54,4 +55,78 @@ public class CommandHelpers { }); return args; } + + public static float parseRelative(String input, Float current) { + if (input.contains("~")) { // Relative + if (!input.equals("~")) { // Relative with offset + current += Float.parseFloat(input.replace("~", "")); + } // Else no offset, no modification + } else { // Absolute + current = Float.parseFloat(input); + } + return current; + } + + public static Position parsePosition( + String inputX, String inputY, String inputZ, Position curPos, Position curRot) { + Position offset = new Position(); + Position target = new Position(curPos); + if (inputX.contains("~")) { // Relative + if (!inputX.equals("~")) { // Relative with offset + target.addX(Float.parseFloat(inputX.replace("~", ""))); + } + } else if (inputX.contains("^")) { + if (!inputX.equals("^")) { + offset.setX(Float.parseFloat(inputX.replace("^", ""))); + } + } else { // Absolute + target.setX(Float.parseFloat(inputX)); + } + + if (inputY.contains("~")) { // Relative + if (!inputY.equals("~")) { // Relative with offset + target.addY(Float.parseFloat(inputY.replace("~", ""))); + } + } else if (inputY.contains("^")) { + if (!inputY.equals("^")) { + offset.setY(Float.parseFloat(inputY.replace("^", ""))); + } + } else { // Absolute + target.setY(Float.parseFloat(inputY)); + } + + if (inputZ.contains("~")) { // Relative + if (!inputZ.equals("~")) { // Relative with offset + target.addZ(Float.parseFloat(inputZ.replace("~", ""))); + } + } else if (inputZ.contains("^")) { + if (!inputZ.equals("^")) { + offset.setZ(Float.parseFloat(inputZ.replace("^", ""))); + } + } else { // Absolute + target.setZ(Float.parseFloat(inputZ)); + } + + if (!offset.equal3d(Position.ZERO)) { + return calculateOffset(target, curRot, offset); + } else { + return target; + } + } + + public static Position calculateOffset(Position pos, Position rot, Position offset) { + // Degrees to radians + float angleZ = (float) Math.toRadians(rot.getY()); + float angleX = (float) Math.toRadians(rot.getY() + 90); + + // Calculate offset based on current position and rotation + return new Position( + pos.getX() + + offset.getZ() * (float) Math.sin(angleZ) + + offset.getX() * (float) Math.sin(angleX), + pos.getY() + offset.getY(), + pos.getZ() + + offset.getZ() * (float) Math.cos(angleZ) + + offset.getX() * (float) Math.cos(angleX)); + } } diff --git a/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java b/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java index f815cc4c0..d62f8b854 100644 --- a/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java @@ -21,9 +21,9 @@ import lombok.Setter; label = "spawn", aliases = {"drop", "s"}, usage = { - " [x] [blk] [grp] [cfg] ", - " [x] [state] [maxhp] [hp(0 for infinite)] [atk] [def] [blk] [grp] [cfg] ", - " [x] [lv] [ai] [maxhp] [hp(0 for infinite)] [atk] [def] [blk] [grp] [cfg] " + " [x] [blk] [grp] [cfg] [ ] [ ]", + " [x] [state] [maxhp] [hp(0 for infinite)] [atk] [def] [blk] [grp] [cfg] [ ] [ ]", + " [x] [lv] [ai] [maxhp] [hp(0 for infinite)] [atk] [def] [blk] [grp] [cfg] [ ] [ ]" }, permission = "server.spawn", permissionTargeted = "server.spawn.others") @@ -53,14 +53,23 @@ public final class SpawnCommand implements CommandHandler { sendUsageMessage(sender); // Reachable if someone does `/give lv90` or similar throw new IllegalArgumentException(); } + + Position pos = new Position(targetPlayer.getPosition()); + Position rot = new Position(targetPlayer.getRotation()); + switch (args.size()) { + case 7: + try { + rot.setX(CommandHelpers.parseRelative(args.get(4), rot.getX())); + rot.setY(CommandHelpers.parseRelative(args.get(5), rot.getY())); + rot.setZ(CommandHelpers.parseRelative(args.get(6), rot.getZ())); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage( + sender, translate(sender, "commands.execution.argument_error")); + } // Fallthrough case 4: try { - float x, y, z; - x = Float.parseFloat(args.get(1)); - y = Float.parseFloat(args.get(2)); - z = Float.parseFloat(args.get(3)); - param.pos = new Position(x, y, z); + pos = CommandHelpers.parsePosition(args.get(1), args.get(2), args.get(3), pos, rot); } catch (NumberFormatException ignored) { CommandHandler.sendMessage( sender, translate(sender, "commands.execution.argument_error")); @@ -77,6 +86,8 @@ public final class SpawnCommand implements CommandHandler { sendUsageMessage(sender); return; } + param.pos = pos; + param.rot = rot; MonsterData monsterData = GameData.getMonsterDataMap().get(param.id); GadgetData gadgetData = GameData.getGadgetDataMap().get(param.id); @@ -102,12 +113,8 @@ public final class SpawnCommand implements CommandHandler { } double maxRadius = Math.sqrt(param.amount * 0.2 / Math.PI); - if (param.pos == null) { - param.pos = targetPlayer.getPosition(); - } - for (int i = 0; i < param.amount; i++) { - Position pos = GetRandomPositionInCircle(param.pos, maxRadius).addY(3); + pos = GetRandomPositionInCircle(param.pos, maxRadius).addY(3); GameEntity entity = null; if (itemData != null) { entity = createItem(itemData, param, pos); @@ -128,12 +135,12 @@ public final class SpawnCommand implements CommandHandler { } private EntityItem createItem(ItemData itemData, SpawnParameters param, Position pos) { - return new EntityItem(param.scene, null, itemData, pos, 1, true); + return new EntityItem(param.scene, null, itemData, pos, param.rot, 1, true); } private EntityMonster createMonster( MonsterData monsterData, SpawnParameters param, Position pos) { - var entity = new EntityMonster(param.scene, monsterData, pos, param.lvl); + var entity = new EntityMonster(param.scene, monsterData, pos, param.rot, param.lvl); if (param.ai != -1) { entity.setAiId(param.ai); } @@ -144,16 +151,13 @@ public final class SpawnCommand implements CommandHandler { GadgetData gadgetData, SpawnParameters param, Position pos, Player targetPlayer) { EntityBaseGadget entity; if (gadgetData.getType() == EntityType.Vehicle) { - entity = - new EntityVehicle( - param.scene, targetPlayer, param.id, 0, pos, targetPlayer.getRotation()); + entity = new EntityVehicle(param.scene, targetPlayer, param.id, 0, pos, param.rot); } else { - entity = new EntityGadget(param.scene, param.id, pos, targetPlayer.getRotation()); + entity = new EntityGadget(param.scene, param.id, pos, param.rot); if (param.state != -1) { ((EntityGadget) entity).setState(param.state); } } - return entity; } @@ -207,6 +211,7 @@ public final class SpawnCommand implements CommandHandler { @Setter public int def = -1; @Setter public int ai = -1; @Setter public Position pos = null; + @Setter public Position rot = null; public Scene scene = null; } } diff --git a/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java b/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java index f75acb42a..fddd1c20b 100644 --- a/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java @@ -16,24 +16,10 @@ import java.util.List; permissionTargeted = "player.teleport.others") public final class TeleportCommand implements CommandHandler { - private float parseRelative( - String input, Float current) { // TODO: Maybe this will be useful elsewhere later - if (input.contains("~")) { // Relative - if (!input.equals("~")) { // Relative with offset - current += Float.parseFloat(input.replace("~", "")); - } // Else no offset, no modification - } else { // Absolute - current = Float.parseFloat(input); - } - return current; - } - @Override public void execute(Player sender, Player targetPlayer, List args) { - Position pos = targetPlayer.getPosition(); - float x = pos.getX(); - float y = pos.getY(); - float z = pos.getZ(); + Position pos = new Position(targetPlayer.getPosition()); + Position rot = new Position(targetPlayer.getRotation()); int sceneId = targetPlayer.getSceneId(); switch (args.size()) { @@ -46,9 +32,7 @@ public final class TeleportCommand implements CommandHandler { } // Fallthrough case 3: try { - x = this.parseRelative(args.get(0), x); - y = this.parseRelative(args.get(1), y); - z = this.parseRelative(args.get(2), z); + pos = CommandHelpers.parsePosition(args.get(0), args.get(1), args.get(2), pos, rot); } catch (NumberFormatException ignored) { CommandHandler.sendMessage( sender, translate(sender, "commands.teleport.invalid_position")); @@ -59,11 +43,10 @@ public final class TeleportCommand implements CommandHandler { return; } - Position target_pos = new Position(x, y, z); boolean result = targetPlayer .getWorld() - .transferPlayerToScene(targetPlayer, sceneId, TeleportType.COMMAND, target_pos); + .transferPlayerToScene(targetPlayer, sceneId, TeleportType.COMMAND, pos); if (!result) { CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.exists_error")); @@ -71,7 +54,13 @@ public final class TeleportCommand implements CommandHandler { CommandHandler.sendMessage( sender, translate( - sender, "commands.teleport.success", targetPlayer.getNickname(), x, y, z, sceneId)); + sender, + "commands.teleport.success", + targetPlayer.getNickname(), + pos.getX(), + pos.getY(), + pos.getZ(), + sceneId)); } } } diff --git a/src/main/java/emu/grasscutter/data/DataLoader.java b/src/main/java/emu/grasscutter/data/DataLoader.java index 04b643606..e6ea19068 100644 --- a/src/main/java/emu/grasscutter/data/DataLoader.java +++ b/src/main/java/emu/grasscutter/data/DataLoader.java @@ -1,8 +1,6 @@ package emu.grasscutter.data; import emu.grasscutter.Grasscutter; -import emu.grasscutter.server.http.handlers.GachaHandler; -import emu.grasscutter.tools.Tools; import emu.grasscutter.utils.*; import java.io.*; import java.nio.file.*; @@ -114,8 +112,6 @@ public class DataLoader { } catch (Exception e) { Grasscutter.getLogger().error("An error occurred while trying to check the data folder.", e); } - - generateGachaMappings(); } private static void checkAndCopyData(String name) { @@ -131,16 +127,4 @@ public class DataLoader { FileUtils.copyResource("/defaults/data/" + name, filePath.toString()); } } - - private static void generateGachaMappings() { - var path = GachaHandler.getGachaMappingsPath(); - if (!Files.exists(path)) { - try { - Grasscutter.getLogger().debug("Creating default '" + path + "' data"); - Tools.createGachaMappings(path); - } catch (Exception exception) { - Grasscutter.getLogger().warn("Failed to create gacha mappings. \n" + exception); - } - } - } } diff --git a/src/main/java/emu/grasscutter/data/GameData.java b/src/main/java/emu/grasscutter/data/GameData.java index 24b9cbb7e..2f9f7763b 100644 --- a/src/main/java/emu/grasscutter/data/GameData.java +++ b/src/main/java/emu/grasscutter/data/GameData.java @@ -214,6 +214,14 @@ public final class GameData { private static final Int2ObjectMap cookRecipeDataMap = new Int2ObjectOpenHashMap<>(); + @Getter + private static final Int2ObjectMap coopChapterDataMap = + new Int2ObjectOpenHashMap<>(); + + @Getter + private static final Int2ObjectMap coopPointDataMap = + new Int2ObjectOpenHashMap<>(); + @Getter private static final Int2ObjectMap compoundDataMap = new Int2ObjectOpenHashMap<>(); diff --git a/src/main/java/emu/grasscutter/data/excels/CoopChapterData.java b/src/main/java/emu/grasscutter/data/excels/CoopChapterData.java new file mode 100644 index 000000000..89f27901f --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/CoopChapterData.java @@ -0,0 +1,46 @@ +package emu.grasscutter.data.excels; + +import com.google.gson.annotations.SerializedName; +import emu.grasscutter.data.*; +import java.util.List; +import lombok.*; +import lombok.experimental.FieldDefaults; + +@ResourceType(name = "CoopChapterExcelConfigData.json") +@Getter +@Setter // TODO: remove setters next API break +@FieldDefaults(level = AccessLevel.PRIVATE) +public class CoopChapterData extends GameResource { + @Getter(onMethod_ = @Override) + int id; + + int avatarId; + // int chapterNameTextMapHash; + // int coopPageTitleTextMapHash; + // int chapterSortId; + // int avatarSortId; + // String chapterIcon; + List unlockCond; + // int [] unlockCondTips; + // int openMaterialId; + // int openMaterialNum; + // String beginTimeStr; + // int confidenceValue; + // String pointGraphPath; + // Double graphXRatio; + // Double graphYRatio; + + @Data + @FieldDefaults(level = AccessLevel.PRIVATE) + private static class CoopCondition { + @SerializedName( + value = "_condType", + alternate = {"condType"}) + String type = "COOP_COND_NONE"; + + @SerializedName( + value = "_args", + alternate = {"args"}) + int[] args; + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/CoopPointData.java b/src/main/java/emu/grasscutter/data/excels/CoopPointData.java new file mode 100644 index 000000000..cb71fbd91 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/CoopPointData.java @@ -0,0 +1,24 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.*; +import lombok.*; +import lombok.experimental.FieldDefaults; + +@ResourceType(name = "CoopPointExcelConfigData.json") +@Getter +@Setter // TODO: remove setters next API break +@FieldDefaults(level = AccessLevel.PRIVATE) +public class CoopPointData extends GameResource { + @Getter(onMethod_ = @Override) + int id; + + int chapterId; + String type; + int acceptQuest; + int[] postPointList; + // int pointNameTextMapHash; + // int pointDecTextMapHash; + int pointPosId; + // long photoMaleHash; + // long photoFemaleHash; +} diff --git a/src/main/java/emu/grasscutter/game/HandbookActions.java b/src/main/java/emu/grasscutter/game/HandbookActions.java index 5c946ad9a..60283d8df 100644 --- a/src/main/java/emu/grasscutter/game/HandbookActions.java +++ b/src/main/java/emu/grasscutter/game/HandbookActions.java @@ -241,7 +241,8 @@ public interface HandbookActions { // Create the entity. for (var i = 1; i <= request.getAmount(); i++) { - var entity = new EntityMonster(scene, entityData, player.getPosition(), level); + var entity = + new EntityMonster(scene, entityData, player.getPosition(), player.getRotation(), level); scene.addEntity(entity); } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityHomeAnimal.java b/src/main/java/emu/grasscutter/game/entity/EntityHomeAnimal.java index 40e61c339..5be82c916 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityHomeAnimal.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityHomeAnimal.java @@ -18,8 +18,8 @@ public class EntityHomeAnimal extends EntityMonster implements Rebornable { @Getter private final int rebirthCD; private final AtomicBoolean disappeared = new AtomicBoolean(); - public EntityHomeAnimal(Scene scene, HomeWorldAnimalData data, Position pos) { - super(scene, GameData.getMonsterDataMap().get(data.getMonsterID()), pos, 1); + public EntityHomeAnimal(Scene scene, HomeWorldAnimalData data, Position pos, Position rot) { + super(scene, GameData.getMonsterDataMap().get(data.getMonsterID()), pos, rot, 1); this.rebornPos = pos.clone(); this.rebirth = data.getIsRebirth(); diff --git a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java index 4c4777548..b469925fc 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java @@ -54,14 +54,14 @@ public class EntityMonster extends GameEntity { @Getter private List playerOnBattle; @Nullable @Getter @Setter private SceneMonster metaMonster; - public EntityMonster(Scene scene, MonsterData monsterData, Position pos, int level) { + public EntityMonster(Scene scene, MonsterData monsterData, Position pos, Position rot, int level) { super(scene); this.id = this.getWorld().getNextEntityId(EntityIdType.MONSTER); this.monsterData = monsterData; this.fightProperties = new Int2FloatOpenHashMap(); this.position = new Position(pos); - this.rotation = new Position(); + this.rotation = new Position(rot); this.bornPos = this.getPosition().clone(); this.level = level; this.playerOnBattle = new ArrayList<>(); diff --git a/src/main/java/emu/grasscutter/game/entity/GameEntity.java b/src/main/java/emu/grasscutter/game/entity/GameEntity.java index b7320f57c..6ce1ec1e8 100644 --- a/src/main/java/emu/grasscutter/game/entity/GameEntity.java +++ b/src/main/java/emu/grasscutter/game/entity/GameEntity.java @@ -15,9 +15,8 @@ import emu.grasscutter.scripts.data.controller.EntityController; import emu.grasscutter.server.event.entity.*; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import it.unimi.dsi.fastutil.ints.*; -import lombok.*; - import java.util.*; +import lombok.*; public abstract class GameEntity { @Getter private final Scene scene; @@ -35,7 +34,8 @@ public abstract class GameEntity { @Getter @Setter private boolean lockHP; @Setter(AccessLevel.PROTECTED) - @Getter private boolean isDead = false; + @Getter + private boolean isDead = false; // Lua controller for specific actions @Getter @Setter private EntityController entityController; diff --git a/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java b/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java index 93031537c..111dba9cf 100644 --- a/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java +++ b/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java @@ -107,7 +107,8 @@ public class HomeSceneItem { return new EntityHomeAnimal( scene, GameData.getHomeWorldAnimalDataMap().get(homeAnimalItem.getFurnitureId()), - homeAnimalItem.getSpawnPos()); + homeAnimalItem.getSpawnPos(), + homeAnimalItem.getSpawnRot()); }) .toList(); } diff --git a/src/main/java/emu/grasscutter/game/home/HomeWorld.java b/src/main/java/emu/grasscutter/game/home/HomeWorld.java index 1beee4e81..1200a0f97 100644 --- a/src/main/java/emu/grasscutter/game/home/HomeWorld.java +++ b/src/main/java/emu/grasscutter/game/home/HomeWorld.java @@ -25,7 +25,6 @@ public class HomeWorld extends World { this.home = owner.isOnline() ? owner.getHome() : GameHome.getByUid(owner.getUid()); this.refreshModuleManager(); - server.registerHomeWorld(this); } @Override diff --git a/src/main/java/emu/grasscutter/game/managers/blossom/BlossomActivity.java b/src/main/java/emu/grasscutter/game/managers/blossom/BlossomActivity.java index 5b902d347..d73778cca 100644 --- a/src/main/java/emu/grasscutter/game/managers/blossom/BlossomActivity.java +++ b/src/main/java/emu/grasscutter/game/managers/blossom/BlossomActivity.java @@ -104,7 +104,8 @@ public final class BlossomActivity { var monsterData = GameData.getMonsterDataMap().get((int) entry); var level = scene.getEntityLevel(1, worldLevelOverride); - var entity = new EntityMonster(scene, monsterData, pos.nearby2d(4f), level); + var entity = + new EntityMonster(scene, monsterData, pos.nearby2d(4f), Position.ZERO, level); scene.addEntity(entity); newMonsters.add(entity); } diff --git a/src/main/java/emu/grasscutter/game/player/Player.java b/src/main/java/emu/grasscutter/game/player/Player.java index 25b1f66f0..e7b50c46f 100644 --- a/src/main/java/emu/grasscutter/game/player/Player.java +++ b/src/main/java/emu/grasscutter/game/player/Player.java @@ -1378,14 +1378,6 @@ public class Player implements PlayerHook, FieldFetch { this.getPlayerProgress().setPlayer(this); // Add reference to the player. } - /** - * Invoked when the player selects their avatar. - */ - public void onPlayerBorn() { - Grasscutter.getThreadPool().submit( - this.getQuestManager()::onPlayerBorn); - } - public void onLogin() { // Quest - Commented out because a problem is caused if you log out while this quest is active /* diff --git a/src/main/java/emu/grasscutter/game/player/PlayerProgressManager.java b/src/main/java/emu/grasscutter/game/player/PlayerProgressManager.java index 50acd0d9c..1352594a6 100644 --- a/src/main/java/emu/grasscutter/game/player/PlayerProgressManager.java +++ b/src/main/java/emu/grasscutter/game/player/PlayerProgressManager.java @@ -33,12 +33,9 @@ public final class PlayerProgressManager extends BasePlayerDataManager { public static final Set IGNORED_OPEN_STATES = Set.of( - 1404, // OPEN_STATE_MENGDE_INFUSEDCRYSTAL, causes quest 'Mine Craft' to be given to the + 1404 // OPEN_STATE_MENGDE_INFUSEDCRYSTAL, causes quest 'Mine Craft' to be given to the // player at the start of the game. // This should be removed when city reputation is implemented. - 57 // OPEN_STATE_PERSONAL_LINE, causes the prompt for showing character hangout quests to - // be permanently shown. - // This should be removed when character story quests are implemented. ); // Set of open states that are set per default for all accounts. Can be overwritten by an entry in // `map`. diff --git a/src/main/java/emu/grasscutter/game/player/TeamManager.java b/src/main/java/emu/grasscutter/game/player/TeamManager.java index be298ba38..970cf7d4b 100644 --- a/src/main/java/emu/grasscutter/game/player/TeamManager.java +++ b/src/main/java/emu/grasscutter/game/player/TeamManager.java @@ -1,5 +1,7 @@ package emu.grasscutter.game.player; +import static emu.grasscutter.config.Configuration.GAME_OPTIONS; + import dev.morphia.annotations.*; import emu.grasscutter.*; import emu.grasscutter.data.GameData; @@ -21,12 +23,9 @@ import emu.grasscutter.server.packet.send.*; import emu.grasscutter.utils.Utils; import it.unimi.dsi.fastutil.ints.*; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import lombok.*; - import java.util.*; import java.util.stream.Stream; - -import static emu.grasscutter.config.Configuration.GAME_OPTIONS; +import lombok.*; @Entity public final class TeamManager extends BasePlayerDataManager { diff --git a/src/main/java/emu/grasscutter/game/quest/QuestManager.java b/src/main/java/emu/grasscutter/game/quest/QuestManager.java index 5cf0e02f7..0e9ae04a5 100644 --- a/src/main/java/emu/grasscutter/game/quest/QuestManager.java +++ b/src/main/java/emu/grasscutter/game/quest/QuestManager.java @@ -221,14 +221,11 @@ public final class QuestManager extends BasePlayerManager { this.player.sendPacket(new PacketGivingRecordNotify(this.getGivingRecords())); } - public void onPlayerBorn() { + public void onLogin() { if (this.isQuestingEnabled()) { this.enableQuests(); this.sendGivingRecords(); } - } - - public void onLogin() { List activeQuests = getActiveMainQuests(); List activeSubs = new ArrayList<>(activeQuests.size()); diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionMainCoopStart.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionMainCoopStart.java new file mode 100644 index 000000000..bfd7ee176 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionMainCoopStart.java @@ -0,0 +1,21 @@ +package emu.grasscutter.game.quest.conditions; + +import emu.grasscutter.data.excels.quest.QuestData; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.quest.QuestValueCond; +import emu.grasscutter.game.quest.enums.QuestCond; + +@QuestValueCond(QuestCond.QUEST_COND_MAIN_COOP_START) +public class ConditionMainCoopStart extends BaseCondition { + + @Override + public boolean execute( + Player owner, + QuestData questData, + QuestData.QuestAcceptCondition condition, + String paramStr, + int... params) { + return condition.getParam()[0] == params[0] + && (condition.getParam()[1] == 0 || condition.getParam()[1] == params[1]); + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/enums/QuestCond.java b/src/main/java/emu/grasscutter/game/quest/enums/QuestCond.java index 7b43c442d..24d8fcf07 100644 --- a/src/main/java/emu/grasscutter/game/quest/enums/QuestCond.java +++ b/src/main/java/emu/grasscutter/game/quest/enums/QuestCond.java @@ -54,7 +54,7 @@ public enum QuestCond implements QuestTrigger { QUEST_COND_QUEST_GLOBAL_VAR_LESS(46), QUEST_COND_PERSONAL_LINE_UNLOCK(47), QUEST_COND_CITY_REPUTATION_REQUEST(48), // missing - QUEST_COND_MAIN_COOP_START(49), // missing + QUEST_COND_MAIN_COOP_START(49), QUEST_COND_MAIN_COOP_ENTER_SAVE_POINT(50), // missing QUEST_COND_CITY_REPUTATION_LEVEL(51), // missing, only NPC groups QUEST_COND_CITY_REPUTATION_UNLOCK(52), // missing, currently unused diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index 481bcbe8f..9e7a1c7db 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -816,8 +816,8 @@ public class Scene { int level = this.getEntityLevel(entry.getLevel(), worldLevelOverride); - EntityMonster monster = new EntityMonster(this, data, entry.getPos(), level); - monster.getRotation().set(entry.getRot()); + EntityMonster monster = + new EntityMonster(this, data, entry.getPos(), entry.getRot(), level); monster.setGroupId(entry.getGroup().getGroupId()); monster.setPoseId(entry.getPoseId()); monster.setConfigId(entry.getConfigId()); diff --git a/src/main/java/emu/grasscutter/game/world/World.java b/src/main/java/emu/grasscutter/game/world/World.java index 5664e1282..707fe6db6 100644 --- a/src/main/java/emu/grasscutter/game/world/World.java +++ b/src/main/java/emu/grasscutter/game/world/World.java @@ -72,6 +72,8 @@ public class World implements Iterable { this.scenes = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>()); this.entity = new EntityWorld(this); this.lastUpdateTime = System.currentTimeMillis(); + + server.registerWorld(this); } public int getLevelEntityId() { @@ -396,37 +398,50 @@ public class World implements Iterable { return false; } - Scene oldScene = null; - if (player.getScene() != null) { - oldScene = player.getScene(); + Scene oldScene = player.getScene(); + var newScene = this.getSceneById(teleportProperties.getSceneId()); + // Move directly in the same scene. + if (newScene == oldScene && teleportProperties.getTeleportType() == TeleportType.COMMAND) { + // Set player position and rotation + if (teleportProperties.getTeleportTo() != null) { + player.getPosition().set(teleportProperties.getTeleportTo()); + } + if (teleportProperties.getTeleportRot() != null) { + player.getRotation().set(teleportProperties.getTeleportRot()); + } + player.sendPacket(new PacketSceneEntityAppearNotify(player)); + return true; + } + + if (oldScene != null) { // Don't deregister scenes if the player is going to tp back into them - if (oldScene.getId() == teleportProperties.getSceneId()) { + if (oldScene == newScene) { oldScene.setDontDestroyWhenEmpty(true); } - oldScene.removePlayer(player); } - var newScene = this.getSceneById(teleportProperties.getSceneId()); - newScene.addPlayer(player); + if (newScene != null) { + newScene.addPlayer(player); - player.getTeamManager().applyAbilities(newScene); + player.getTeamManager().applyAbilities(newScene); - // Dungeon - // Dungeon system is handling this already - // if(dungeonData!=null){ - // var dungeonManager = new DungeonManager(newScene, dungeonData); - // dungeonManager.startDungeon(); - // } + // Dungeon + // Dungeon system is handling this already + // if(dungeonData!=null){ + // var dungeonManager = new DungeonManager(newScene, dungeonData); + // dungeonManager.startDungeon(); + // } - SceneConfig config = newScene.getScriptManager().getConfig(); - if (teleportProperties.getTeleportTo() == null && config != null) { - if (config.born_pos != null) { - teleportProperties.setTeleportTo(config.born_pos); - } - if (config.born_rot != null) { - teleportProperties.setTeleportRot(config.born_rot); + SceneConfig config = newScene.getScriptManager().getConfig(); + if (teleportProperties.getTeleportTo() == null && config != null) { + if (config.born_pos != null) { + teleportProperties.setTeleportTo(config.born_pos); + } + if (config.born_rot != null) { + teleportProperties.setTeleportRot(config.born_rot); + } } } @@ -438,7 +453,7 @@ public class World implements Iterable { player.getRotation().set(teleportProperties.getTeleportRot()); } - if (oldScene != null && newScene != oldScene) { + if (oldScene != null && newScene != null && newScene != oldScene) { newScene.setPrevScenePoint(oldScene.getPrevScenePoint()); oldScene.setDontDestroyWhenEmpty(false); } diff --git a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java index 33cadecf8..5ca90345d 100644 --- a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java +++ b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java @@ -1037,8 +1037,7 @@ public class SceneScriptManager { } // Spawn mob - EntityMonster entity = new EntityMonster(getScene(), data, monster.pos, level); - entity.getRotation().set(monster.rot); + EntityMonster entity = new EntityMonster(getScene(), data, monster.pos, monster.rot, level); entity.setGroupId(groupId); entity.setBlockId(blockId); entity.setConfigId(monster.config_id); diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index 1787f93ea..ecd6b1425 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -311,11 +311,6 @@ public final class GameServer extends KcpServer implements Iterable { world.save(); // Save the player's world } - public void registerHomeWorld(HomeWorld homeWorld) { - this.getHomeWorlds().put(homeWorld.getOwnerUid(), homeWorld); - this.registerWorld(homeWorld); - } - public HomeWorld getHomeWorldOrCreate(Player owner) { return this.getHomeWorlds() .computeIfAbsent(owner.getUid(), (uid) -> new HomeWorld(this, owner)); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerCancelCoopTaskReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCancelCoopTaskReq.java new file mode 100644 index 000000000..ebbb8fdd4 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCancelCoopTaskReq.java @@ -0,0 +1,21 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.net.packet.*; +import emu.grasscutter.net.proto.CancelCoopTaskReqOuterClass; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketCancelCoopTaskRsp; + +@Opcodes(PacketOpcodes.CancelCoopTaskReq) +public class HandlerCancelCoopTaskReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + CancelCoopTaskReqOuterClass.CancelCoopTaskReq req = + CancelCoopTaskReqOuterClass.CancelCoopTaskReq.parseFrom(payload); + var chapterId = req.getChapterId(); + Grasscutter.getLogger().warn("Call to unimplemented packet CancelCoopTaskReq"); + // TODO: Actually cancel the quests. + session.send(new PacketCancelCoopTaskRsp(chapterId)); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPersonalLineAllDataReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPersonalLineAllDataReq.java index 278bc7b54..4afbf5573 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPersonalLineAllDataReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPersonalLineAllDataReq.java @@ -2,6 +2,7 @@ package emu.grasscutter.server.packet.recv; import emu.grasscutter.net.packet.*; import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketCoopDataNotify; import emu.grasscutter.server.packet.send.PacketPersonalLineAllDataRsp; @Opcodes(PacketOpcodes.PersonalLineAllDataReq) @@ -12,5 +13,7 @@ public class HandlerPersonalLineAllDataReq extends PacketHandler { session.send( new PacketPersonalLineAllDataRsp( session.getPlayer().getQuestManager().getMainQuests().values())); + // TODO: this should maybe be at player login? + session.send(new PacketCoopDataNotify()); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuestCreateEntityReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuestCreateEntityReq.java index 2c2b5015c..62582d13e 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuestCreateEntityReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuestCreateEntityReq.java @@ -61,7 +61,7 @@ public class HandlerQuestCreateEntityReq extends PacketHandler { val monsterId = entity.getMonsterId(); val level = entity.getLevel(); MonsterData monsterData = GameData.getMonsterDataMap().get(monsterId); - gameEntity = new EntityMonster(scene, monsterData, pos, level); + gameEntity = new EntityMonster(scene, monsterData, pos, rot, level); } case NPC_ID -> {} } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java index 1ce2e20eb..19e0852cd 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java @@ -69,7 +69,6 @@ public class HandlerSetPlayerBornDataReq extends PacketHandler { // Login done session.getPlayer().onLogin(); - session.getPlayer().onPlayerBorn(); // Born resp packet session.send(new BasePacket(PacketOpcodes.SetPlayerBornDataRsp)); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerStartCoopPointReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerStartCoopPointReq.java new file mode 100644 index 000000000..63020c10b --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerStartCoopPointReq.java @@ -0,0 +1,31 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.game.quest.enums.QuestCond; +import emu.grasscutter.net.packet.*; +import emu.grasscutter.net.proto.StartCoopPointReqOuterClass; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketStartCoopPointRsp; + +@Opcodes(PacketOpcodes.StartCoopPointReq) +public class HandlerStartCoopPointReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + StartCoopPointReqOuterClass.StartCoopPointReq req = + StartCoopPointReqOuterClass.StartCoopPointReq.parseFrom(payload); + var coopPoint = req.getCoopPoint(); + + var coopPointData = + GameData.getCoopPointDataMap().values().stream() + .filter(i -> i.getId() == coopPoint) + .toList(); + if (!coopPointData.isEmpty()) { + var player = session.getPlayer(); + var questManager = player.getQuestManager(); + questManager.queueEvent( + QuestCond.QUEST_COND_MAIN_COOP_START, coopPointData.get(0).getChapterId(), 0); + } + session.send(new PacketStartCoopPointRsp(coopPoint)); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketCancelCoopTaskRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketCancelCoopTaskRsp.java new file mode 100644 index 000000000..88e7b8681 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketCancelCoopTaskRsp.java @@ -0,0 +1,16 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.*; +import emu.grasscutter.net.proto.CancelCoopTaskRspOuterClass; + +public class PacketCancelCoopTaskRsp extends BasePacket { + + public PacketCancelCoopTaskRsp(int chapterId) { + super(PacketOpcodes.SetCoopChapterViewedRsp); + + CancelCoopTaskRspOuterClass.CancelCoopTaskRsp proto = + CancelCoopTaskRspOuterClass.CancelCoopTaskRsp.newBuilder().setChapterId(chapterId).build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketCoopDataNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketCoopDataNotify.java new file mode 100644 index 000000000..004f636dc --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketCoopDataNotify.java @@ -0,0 +1,49 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.net.packet.*; +import emu.grasscutter.net.proto.CoopChapterOuterClass; +import emu.grasscutter.net.proto.CoopDataNotifyOuterClass; +import emu.grasscutter.net.proto.CoopPointOuterClass; + +public class PacketCoopDataNotify extends BasePacket { + + public PacketCoopDataNotify() { + super(PacketOpcodes.CoopDataNotify); + + var proto = CoopDataNotifyOuterClass.CoopDataNotify.newBuilder(); + proto.setIsHaveProgress(false); + + // TODO: implement: determine the actual current progress point. + // Add every chapter and add the start point to each chapter regardless of actual progress. + GameData.getCoopChapterDataMap() + .values() + .forEach( + i -> { + var chapter = CoopChapterOuterClass.CoopChapter.newBuilder(); + chapter.setId(i.getId()); + + // TODO: implement: look at unlockCond to determine what state each chapter should be + // in. + // Set every chapter to "Accept" regardless of accept conditions. + chapter.setStateValue(3); // 3 == STATE_ACCEPT + + var point = CoopPointOuterClass.CoopPoint.newBuilder(); + var pointList = + GameData.getCoopPointDataMap().values().stream() + .filter( + j -> j.getChapterId() == i.getId() && j.getType().equals("POINT_START")) + .toList(); + + if (!pointList.isEmpty()) { + int pointId = pointList.get(0).getId(); + point.setId(pointId); + chapter.addCoopPointList(point); + } + + proto.addChapterList(chapter); + }); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketStartCoopPointRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketStartCoopPointRsp.java new file mode 100644 index 000000000..681dd119f --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketStartCoopPointRsp.java @@ -0,0 +1,16 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.*; +import emu.grasscutter.net.proto.StartCoopPointRspOuterClass; + +public class PacketStartCoopPointRsp extends BasePacket { + + public PacketStartCoopPointRsp(int coopPoint) { + super(PacketOpcodes.StartCoopPointRsp); + + StartCoopPointRspOuterClass.StartCoopPointRsp proto = + StartCoopPointRspOuterClass.StartCoopPointRsp.newBuilder().setCoopPoint(coopPoint).build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/tools/Tools.java b/src/main/java/emu/grasscutter/tools/Tools.java index a611acb89..ea88a5296 100644 --- a/src/main/java/emu/grasscutter/tools/Tools.java +++ b/src/main/java/emu/grasscutter/tools/Tools.java @@ -10,6 +10,7 @@ import emu.grasscutter.data.common.ItemUseData; import emu.grasscutter.data.excels.*; import emu.grasscutter.data.excels.achievement.AchievementData; import emu.grasscutter.data.excels.avatar.AvatarData; +import emu.grasscutter.server.http.handlers.GachaHandler; import emu.grasscutter.utils.*; import emu.grasscutter.utils.lang.Language; import emu.grasscutter.utils.lang.Language.TextStrings; @@ -311,8 +312,19 @@ public final class Tools { return sbs.stream().map(StringBuilder::toString).toList(); } + public static void generateGachaMappings() { + var path = GachaHandler.getGachaMappingsPath(); + if (!Files.exists(path)) { + try { + Grasscutter.getLogger().debug("Creating default '" + path + "' data"); + Tools.createGachaMappings(path); + } catch (Exception exception) { + Grasscutter.getLogger().warn("Failed to create gacha mappings. \n" + exception); + } + } + } + public static void createGachaMappings(Path location) throws IOException { - ResourceLoader.loadResources(); List jsons = createGachaMappingJsons(); var usedLocales = new HashSet(); StringBuilder sb = new StringBuilder("mappings = {\n");