Continue updating/refactoring classes

Most code is matched from `Grasscutter-Quests`.
This commit is contained in:
KingRainbow44 2023-04-01 22:17:10 -04:00
parent 772532515e
commit 9fbb7fb3be
No known key found for this signature in database
GPG Key ID: FC2CB64B00D257BE
97 changed files with 14397 additions and 12921 deletions

View File

@ -117,7 +117,7 @@ public final class EntityCommand implements CommandHandler {
} }
private void callHPEvents(GameEntity entity, EntityDamageEvent event) { private void callHPEvents(GameEntity entity, EntityDamageEvent event) {
entity.callLuaHPEvent(event); entity.runLuaCallbacks(event);
} }
private void setFightProperty( private void setFightProperty(

View File

@ -180,9 +180,9 @@ public final class SetPropCommand implements CommandHandler {
private boolean setBool(Player sender, Player targetPlayer, PseudoProp pseudoProp, int value) { private boolean setBool(Player sender, Player targetPlayer, PseudoProp pseudoProp, int value) {
boolean enabled = boolean enabled =
switch (pseudoProp) { switch (pseudoProp) {
case GOD_MODE -> targetPlayer.inGodmode(); case GOD_MODE -> targetPlayer.isInGodMode();
case UNLIMITED_STAMINA -> targetPlayer.getUnlimitedStamina(); case UNLIMITED_STAMINA -> targetPlayer.isUnlimitedStamina();
case UNLIMITED_ENERGY -> !targetPlayer.getEnergyManager().getEnergyUsage(); case UNLIMITED_ENERGY -> !targetPlayer.getEnergyManager().isEnergyUsage();
default -> false; default -> false;
}; };
enabled = enabled =
@ -194,7 +194,7 @@ public final class SetPropCommand implements CommandHandler {
switch (pseudoProp) { switch (pseudoProp) {
case GOD_MODE: case GOD_MODE:
targetPlayer.setGodmode(enabled); targetPlayer.setInGodMode(enabled);
break; break;
case UNLIMITED_STAMINA: case UNLIMITED_STAMINA:
targetPlayer.setUnlimitedStamina(enabled); targetPlayer.setUnlimitedStamina(enabled);

View File

@ -6,11 +6,12 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.Grasscutter.ServerDebugMode; import emu.grasscutter.Grasscutter.ServerDebugMode;
import emu.grasscutter.Grasscutter.ServerRunMode; import emu.grasscutter.Grasscutter.ServerRunMode;
import emu.grasscutter.utils.JsonUtils; import emu.grasscutter.utils.JsonUtils;
import lombok.NoArgsConstructor;
import java.util.Set;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
import java.util.Set;
import static emu.grasscutter.Grasscutter.config; import static emu.grasscutter.Grasscutter.config;
@ -18,14 +19,6 @@ import static emu.grasscutter.Grasscutter.config;
* *when your JVM fails* * *when your JVM fails*
*/ */
public class ConfigContainer { public class ConfigContainer {
public Structure folderStructure = new Structure();
public Database databaseInfo = new Database();
public Language language = new Language();
public Account account = new Account();
public Server server = new Server();
// DO NOT. TOUCH. THE VERSION NUMBER.
public int version = version();
private static int version() { private static int version() {
return 4; return 4;
} }
@ -40,8 +33,7 @@ public class ConfigContainer {
Grasscutter.getLogger().info("Updating legacy .."); Grasscutter.getLogger().info("Updating legacy ..");
Grasscutter.saveConfig(null); Grasscutter.saveConfig(null);
} }
} catch (Exception ignored) { } catch (Exception ignored) { }
}
var existing = config.version; var existing = config.version;
var latest = version(); var latest = version();
@ -59,8 +51,7 @@ public class ConfigContainer {
} catch (Exception exception) { } catch (Exception exception) {
Grasscutter.getLogger().error("Failed to update a configuration field.", exception); Grasscutter.getLogger().error("Failed to update a configuration field.", exception);
} }
}); }); updated.version = version();
updated.version = version();
try { // Save configuration & reload. try { // Save configuration & reload.
Grasscutter.saveConfig(updated); Grasscutter.saveConfig(updated);
@ -70,6 +61,15 @@ public class ConfigContainer {
} }
} }
public Structure folderStructure = new Structure();
public Database databaseInfo = new Database();
public Language language = new Language();
public Account account = new Account();
public Server server = new Server();
// DO NOT. TOUCH. THE VERSION NUMBER.
public int version = version();
/* Option containers. */ /* Option containers. */
public static class Database { public static class Database {
@ -145,7 +145,7 @@ public class ConfigContainer {
public int accessPort = 0; public int accessPort = 0;
/* Entities within a certain range will be loaded for the player */ /* Entities within a certain range will be loaded for the player */
public int loadEntitiesForPlayerRange = 100; public int loadEntitiesForPlayerRange = 300;
public boolean enableScriptInBigWorld = false; public boolean enableScriptInBigWorld = false;
public boolean enableConsole = true; public boolean enableConsole = true;
@ -154,13 +154,24 @@ public class ConfigContainer {
/* Controls whether packets should be logged in console or not */ /* Controls whether packets should be logged in console or not */
public ServerDebugMode logPackets = ServerDebugMode.NONE; public ServerDebugMode logPackets = ServerDebugMode.NONE;
/* Show packet payload in console or no (in any case the payload is shown in encrypted view) */ /* Show packet payload in console or no (in any case the payload is shown in encrypted view) */
public Boolean isShowPacketPayload = false; public boolean isShowPacketPayload = false;
/* Show annoying loop packets or no */ /* Show annoying loop packets or no */
public Boolean isShowLoopPackets = false; public boolean isShowLoopPackets = false;
public boolean cacheSceneEntitiesEveryRun = false;
public GameOptions gameOptions = new GameOptions(); public GameOptions gameOptions = new GameOptions();
public JoinOptions joinOptions = new JoinOptions(); public JoinOptions joinOptions = new JoinOptions();
public ConsoleAccount serverAccount = new ConsoleAccount(); public ConsoleAccount serverAccount = new ConsoleAccount();
public VisionOptions[] visionOptions = new VisionOptions[] {
new VisionOptions("VISION_LEVEL_NORMAL" , 80 , 20),
new VisionOptions("VISION_LEVEL_LITTLE_REMOTE" , 16 , 40),
new VisionOptions("VISION_LEVEL_REMOTE" , 1000 , 250),
new VisionOptions("VISION_LEVEL_SUPER" , 4000 , 1000),
new VisionOptions("VISION_LEVEL_NEARBY" , 40 , 20),
new VisionOptions("VISION_LEVEL_SUPER_NEARBY" , 20 , 20)
};
} }
/* Data containers. */ /* Data containers. */
@ -188,10 +199,10 @@ public class ConfigContainer {
public ServerDebugMode logPackets = ServerDebugMode.ALL; public ServerDebugMode logPackets = ServerDebugMode.ALL;
/* Show packet payload in console or no (in any case the payload is shown in encrypted view) */ /* Show packet payload in console or no (in any case the payload is shown in encrypted view) */
public Boolean isShowPacketPayload = false; public boolean isShowPacketPayload = false;
/* Show annoying loop packets or no */ /* Show annoying loop packets or no */
public Boolean isShowLoopPackets = false; public boolean isShowLoopPackets = false;
/* Controls whether http requests should be logged in console or not */ /* Controls whether http requests should be logged in console or not */
public ServerDebugMode logRequests = ServerDebugMode.ALL; public ServerDebugMode logRequests = ServerDebugMode.ALL;
@ -224,6 +235,7 @@ public class ConfigContainer {
public boolean staminaUsage = true; public boolean staminaUsage = true;
public boolean energyUsage = true; public boolean energyUsage = true;
public boolean fishhookTeleport = true; public boolean fishhookTeleport = true;
public boolean questing = false;
public ResinOptions resinOptions = new ResinOptions(); public ResinOptions resinOptions = new ResinOptions();
public Rates rates = new Rates(); public Rates rates = new Rates();
@ -253,6 +265,18 @@ public class ConfigContainer {
} }
} }
public static class VisionOptions {
public String name;
public int visionRange;
public int gridWidth;
public VisionOptions(String name, int visionRange, int gridWidth) {
this.name = name;
this.visionRange = visionRange;
this.gridWidth = gridWidth;
}
}
public static class JoinOptions { public static class JoinOptions {
public int[] welcomeEmotes = {2007, 1002, 4010}; public int[] welcomeEmotes = {2007, 1002, 4010};
public String welcomeMessage = "Welcome to a Grasscutter server."; public String welcomeMessage = "Welcome to a Grasscutter server.";
@ -292,13 +316,13 @@ public class ConfigContainer {
/* Objects. */ /* Objects. */
@NoArgsConstructor
public static class Region { public static class Region {
public String Name = "os_usa"; public String Name = "os_usa";
public String Title = "Grasscutter"; public String Title = "Grasscutter";
public String Ip = "127.0.0.1"; public String Ip = "127.0.0.1";
public int Port = 22102; public int Port = 22102;
public Region() {
}
public Region( public Region(
String name, String title, String name, String title,
String address, int port String address, int port

View File

@ -48,7 +48,10 @@ public final class GameData {
@Getter @Getter
private static final Map<String, AbilityModifierEntry> abilityModifiers = new HashMap<>(); private static final Map<String, AbilityModifierEntry> abilityModifiers = new HashMap<>();
@Getter private static final Map<String, ConfigGadget> gadgetConfigData = new HashMap<>(); @Getter private static final Map<String, ConfigEntityAvatar> avatarConfigData = new HashMap<>();
@Getter private static final Map<String, ConfigEntityGadget> gadgetConfigData = new HashMap<>();
@Getter private static final Map<String, ConfigEntityMonster> monsterConfigData = new HashMap<>();
@Getter private static final Map<String, OpenConfigEntry> openConfigEntries = new HashMap<>(); @Getter private static final Map<String, OpenConfigEntry> openConfigEntries = new HashMap<>();
private static final Int2ObjectMap<MainQuestData> mainQuestData = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<MainQuestData> mainQuestData = new Int2ObjectOpenHashMap<>();
@ -102,6 +105,8 @@ public final class GameData {
private static final Int2ObjectMap<AvatarCostumeData> avatarCostumeDataMap = private static final Int2ObjectMap<AvatarCostumeData> avatarCostumeDataMap =
new Int2ObjectLinkedOpenHashMap<>(); new Int2ObjectLinkedOpenHashMap<>();
@Getter private static final Int2ObjectMap<AvatarReplaceCostumeData> avatarReplaceCostumeDataMap = new Int2ObjectOpenHashMap<>();
@Getter @Getter
private static final Int2ObjectMap<AvatarCurveData> avatarCurveDataMap = private static final Int2ObjectMap<AvatarCurveData> avatarCurveDataMap =
new Int2ObjectLinkedOpenHashMap<>(); new Int2ObjectLinkedOpenHashMap<>();
@ -221,6 +226,8 @@ public final class GameData {
@Getter @Getter
private static final Int2ObjectMap<GatherData> gatherDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<GatherData> gatherDataMap = new Int2ObjectOpenHashMap<>();
@Getter @Deprecated // This is to prevent people from using this map. This is for the resource loader only!
private static final Int2ObjectMap<GuideTriggerData> guideTriggerDataMap = new Int2ObjectOpenHashMap<>();
@Getter @Getter
private static final Int2ObjectMap<HomeWorldBgmData> homeWorldBgmDataMap = private static final Int2ObjectMap<HomeWorldBgmData> homeWorldBgmDataMap =
@ -246,6 +253,8 @@ public final class GameData {
@Getter @Getter
private static final Int2ObjectMap<MonsterDescribeData> monsterDescribeDataMap = private static final Int2ObjectMap<MonsterDescribeData> monsterDescribeDataMap =
new Int2ObjectOpenHashMap<>(); new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<MonsterSpecialNameData> monsterSpecialNameDataMap =
new Int2ObjectOpenHashMap<>();
@Getter @Getter
private static final Int2ObjectMap<MusicGameBasicData> musicGameBasicDataMap = private static final Int2ObjectMap<MusicGameBasicData> musicGameBasicDataMap =
@ -403,9 +412,8 @@ public final class GameData {
@Getter private static final List<OpenStateData> openStateList = new ArrayList<>(); @Getter private static final List<OpenStateData> openStateList = new ArrayList<>();
@Getter private static final Map<Integer, List<Integer>> scenePointsPerScene = new HashMap<>(); @Getter private static final Map<Integer, List<Integer>> scenePointsPerScene = new HashMap<>();
@Getter private static final Map<String, ScriptSceneData> scriptSceneDataMap = new HashMap<>(); @Getter private static final Map<String, ScriptSceneData> scriptSceneDataMap = new HashMap<>();
@Getter private static final Map<String, GuideTriggerData> guideTriggerDataStringMap = new HashMap<>();
@Getter @Getter private static final Map<String, ConfigLevelEntity> configLevelEntityDataMap = new HashMap<>();
private static final Map<String, ConfigLevelEntity> configLevelEntityDataMap = new HashMap<>();
@Getter @Getter
private static final Int2ObjectMap<IntSet> proudSkillGroupLevels = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<IntSet> proudSkillGroupLevels = new Int2ObjectOpenHashMap<>();

View File

@ -8,6 +8,10 @@ import com.google.gson.annotations.SerializedName;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.binout.*; import emu.grasscutter.data.binout.*;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction; import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
import emu.grasscutter.data.binout.config.ConfigEntityAvatar;
import emu.grasscutter.data.binout.config.ConfigEntityBase;
import emu.grasscutter.data.binout.config.ConfigEntityGadget;
import emu.grasscutter.data.binout.config.ConfigEntityMonster;
import emu.grasscutter.data.common.PointData; import emu.grasscutter.data.common.PointData;
import emu.grasscutter.game.managers.blossom.BlossomConfig; import emu.grasscutter.game.managers.blossom.BlossomConfig;
import emu.grasscutter.game.quest.QuestEncryptionKey; import emu.grasscutter.game.quest.QuestEncryptionKey;
@ -85,6 +89,7 @@ public class ResourceLoader {
if (loadedAll) return; if (loadedAll) return;
Grasscutter.getLogger().info(translate("messages.status.resources.loading")); Grasscutter.getLogger().info(translate("messages.status.resources.loading"));
loadConfigData();
// Load ability lists // Load ability lists
loadAbilityEmbryos(); loadAbilityEmbryos();
loadOpenConfig(); loadOpenConfig();
@ -94,7 +99,6 @@ public class ResourceLoader {
// Process into depots // Process into depots
GameDepot.load(); GameDepot.load();
// Load spawn data and quests // Load spawn data and quests
loadGadgetConfigData();
loadSpawnData(); loadSpawnData();
loadQuests(); loadQuests();
loadScriptSceneData(); loadScriptSceneData();
@ -575,24 +579,44 @@ public class ResourceLoader {
} }
} }
private static void loadGadgetConfigData() { private static void loadConfigData(){
loadConfigData(GameData.getAvatarConfigData(), "BinOutput/Avatar/", ConfigEntityAvatar.class);
loadConfigData(GameData.getMonsterConfigData(), "BinOutput/Monster/", ConfigEntityMonster.class);
loadConfigDataMap(GameData.getGadgetConfigData(), "BinOutput/Gadget/", ConfigEntityGadget.class);
}
private static <T extends ConfigEntityBase> void loadConfigData(Map<String,T> targetMap, String folderPath, Class<T> configClass) {
val className = configClass.getName();
try(val stream = Files.newDirectoryStream(getResourcePath(folderPath), "*.json")) {
stream.forEach(path -> {
try { try {
Files.newDirectoryStream(getResourcePath("BinOutput/Gadget/"), "*.json") val name = path.getFileName().toString().replace(".json", "");
.forEach( targetMap.put(name, JsonUtils.loadToClass(path, configClass));
path -> {
try {
GameData.getGadgetConfigData()
.putAll(JsonUtils.loadToMap(path, String.class, ConfigGadget.class));
} catch (Exception e) { } catch (Exception e) {
Grasscutter.getLogger() Grasscutter.getLogger().error("failed to load {} entries for {}", className, path.toString(), e);
.error("failed to load ConfigGadget entries for " + path.toString(), e);
} }
}); });
Grasscutter.getLogger() Grasscutter.getLogger().debug("Loaded {} {} entries.", GameData.getMonsterConfigData().size(), className);
.debug("Loaded {} ConfigGadget entries.", GameData.getGadgetConfigData().size());
} catch (IOException e) { } catch (IOException e) {
Grasscutter.getLogger().error("Failed to load ConfigGadget folder."); Grasscutter.getLogger().error("Failed to load {} folder.", className);
}
}
private static <T extends ConfigEntityBase> void loadConfigDataMap(Map<String,T> targetMap, String folderPath, Class<T> configClass) {
val className = configClass.getName();
try(val stream = Files.newDirectoryStream(getResourcePath(folderPath), "*.json")) {
stream.forEach(path -> {
try {
targetMap.putAll(JsonUtils.loadToMap(path, String.class, configClass));
} catch (Exception e) {
Grasscutter.getLogger().error("failed to load {} entries for {}", className, path.toString(), e);
}
});
Grasscutter.getLogger().debug("Loaded {} {} entries.", GameData.getMonsterConfigData().size(), className);
} catch (IOException e) {
Grasscutter.getLogger().error("Failed to load {} folder.", className);
} }
} }

View File

@ -1,6 +1,7 @@
package emu.grasscutter.data.common; package emu.grasscutter.data.common;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.dungeon.DailyDungeonData; import emu.grasscutter.data.excels.dungeon.DailyDungeonData;
@ -10,29 +11,25 @@ import it.unimi.dsi.fastutil.ints.IntList;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
public class PointData { public final class PointData {
@Getter @Setter private int id; @Getter @Setter private int id;
private String $type; private String $type;
@Getter private Position tranPos; @Getter private Position tranPos;
@Getter private Position pos;
@Getter private Position rot;
@Getter private Position size;
@SerializedName( @SerializedName(value="dungeonIds", alternate={"JHHFPGJNMIN"})
value = "dungeonIds", @Getter private int[] dungeonIds;
alternate = {"JHHFPGJNMIN"})
@Getter
private int[] dungeonIds;
@SerializedName( @SerializedName(value="dungeonRandomList", alternate={"OIBKFJNBLHO"})
value = "dungeonRandomList", @Getter private int[] dungeonRandomList;
alternate = {"OIBKFJNBLHO"})
@Getter
private int[] dungeonRandomList;
@SerializedName( @SerializedName(value="groupIDs", alternate={"HFOBOOHKBGF"})
value = "tranSceneId", @Getter private int[] groupIDs;
alternate = {"JHBICGBAPIH"})
@Getter @SerializedName(value="tranSceneId", alternate={"JHBICGBAPIH"})
@Setter @Getter @Setter private int tranSceneId;
private int tranSceneId;
public String getType() { public String getType() {
return $type; return $type;

View File

@ -7,7 +7,7 @@ import lombok.Getter;
@ResourceType(name = "GadgetExcelConfigData.json") @ResourceType(name = "GadgetExcelConfigData.json")
@Getter @Getter
public class GadgetData extends GameResource { public final class GadgetData extends GameResource {
@Getter(onMethod_ = @Override) @Getter(onMethod_ = @Override)
private int id; private int id;
@ -17,5 +17,6 @@ public class GadgetData extends GameResource {
private String[] tags; private String[] tags;
private String itemJsonName; private String itemJsonName;
private long nameTextMapHash; private long nameTextMapHash;
private int campID; private int campId;
private String visionLevel;
} }

View File

@ -41,7 +41,7 @@ public class AvatarData extends GameResource {
@Getter @Getter
private int staminaRecoverSpeed; private int staminaRecoverSpeed;
@Getter @Getter
private List<String> candSkillDepotIds; private List<Integer> candSkillDepotIds;
@Getter @Getter
private String avatarIdentityType; private String avatarIdentityType;
@Getter @Getter

View File

@ -1,6 +1,11 @@
package emu.grasscutter.data.excels.monster; package emu.grasscutter.data.excels.monster;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
@ -9,26 +14,12 @@ import emu.grasscutter.data.common.PropGrowCurve;
import emu.grasscutter.data.excels.GadgetData; import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.MonsterType; import emu.grasscutter.game.props.MonsterType;
import java.util.List;
import java.util.Set;
import lombok.Getter; import lombok.Getter;
@ResourceType(name = "MonsterExcelConfigData.json", loadPriority = LoadPriority.LOW) @ResourceType(name = "MonsterExcelConfigData.json", loadPriority = LoadPriority.LOW)
@Getter @Getter
public class MonsterData extends GameResource { public class MonsterData extends GameResource {
public static Set<FightProperty> definedFightProperties = static public Set<FightProperty> definedFightProperties = Set.of(FightProperty.FIGHT_PROP_BASE_HP, FightProperty.FIGHT_PROP_BASE_ATTACK, FightProperty.FIGHT_PROP_BASE_DEFENSE, FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT, FightProperty.FIGHT_PROP_FIRE_SUB_HURT, FightProperty.FIGHT_PROP_ELEC_SUB_HURT, FightProperty.FIGHT_PROP_WATER_SUB_HURT, FightProperty.FIGHT_PROP_GRASS_SUB_HURT, FightProperty.FIGHT_PROP_WIND_SUB_HURT, FightProperty.FIGHT_PROP_ROCK_SUB_HURT, FightProperty.FIGHT_PROP_ICE_SUB_HURT);
Set.of(
FightProperty.FIGHT_PROP_BASE_HP,
FightProperty.FIGHT_PROP_BASE_ATTACK,
FightProperty.FIGHT_PROP_BASE_DEFENSE,
FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT,
FightProperty.FIGHT_PROP_FIRE_SUB_HURT,
FightProperty.FIGHT_PROP_ELEC_SUB_HURT,
FightProperty.FIGHT_PROP_WATER_SUB_HURT,
FightProperty.FIGHT_PROP_GRASS_SUB_HURT,
FightProperty.FIGHT_PROP_WIND_SUB_HURT,
FightProperty.FIGHT_PROP_ROCK_SUB_HURT,
FightProperty.FIGHT_PROP_ICE_SUB_HURT);
@Getter(onMethod_ = @Override) @Getter(onMethod_ = @Override)
private int id; private int id;
@ -51,10 +42,8 @@ public class MonsterData extends GameResource {
@SerializedName("hpBase") @SerializedName("hpBase")
private float baseHp; private float baseHp;
@SerializedName("attackBase") @SerializedName("attackBase")
private float baseAttack; private float baseAttack;
@SerializedName("defenseBase") @SerializedName("defenseBase")
private float baseDefense; private float baseDefense;
@ -74,6 +63,38 @@ public class MonsterData extends GameResource {
private int weaponId; private int weaponId;
private MonsterDescribeData describeData; private MonsterDescribeData describeData;
private int specialNameId; // will only be set if describe data is available
@Override
public void onLoad() {
for (int id : this.equips) {
if (id == 0) {
continue;
}
GadgetData gadget = GameData.getGadgetDataMap().get(id);
if (gadget == null) {
continue;
}
if (gadget.getItemJsonName().equals("Default_MonsterWeapon")) {
this.weaponId = id;
}
}
this.describeData = GameData.getMonsterDescribeDataMap().get(this.getDescribeId());
if (this.describeData == null){
return;
}
for(Entry<Integer, MonsterSpecialNameData> entry: GameData.getMonsterSpecialNameDataMap().entrySet()) {
if (entry.getValue().getSpecialNameLabId() == this.getDescribeData().getSpecialNameLabId()){
this.specialNameId = entry.getKey();
break;
}
}
}
public float getFightProperty(FightProperty prop) { public float getFightProperty(FightProperty prop) {
return switch (prop) { return switch (prop) {
case FIGHT_PROP_BASE_HP -> this.baseHp; case FIGHT_PROP_BASE_HP -> this.baseHp;
@ -91,24 +112,6 @@ public class MonsterData extends GameResource {
}; };
} }
@Override
public void onLoad() {
this.describeData = GameData.getMonsterDescribeDataMap().get(this.getDescribeId());
for (int id : this.equips) {
if (id == 0) {
continue;
}
GadgetData gadget = GameData.getGadgetDataMap().get(id);
if (gadget == null) {
continue;
}
if (gadget.getItemJsonName().equals("Default_MonsterWeapon")) {
this.weaponId = id;
}
}
}
@Getter @Getter
public class HpDrops { public class HpDrops {
private int DropId; private int DropId;

View File

@ -1,5 +1,6 @@
package emu.grasscutter.data.excels.monster; package emu.grasscutter.data.excels.monster;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority; import emu.grasscutter.data.ResourceType.LoadPriority;
@ -12,6 +13,9 @@ public class MonsterDescribeData extends GameResource {
private int id; private int id;
private long nameTextMapHash; private long nameTextMapHash;
private int titleID; @SerializedName(value = "titleId", alternate={"titleID"})
private int specialNameLabID; private int titleId;
@SerializedName(value = "specialNameLabId", alternate={"specialNameLabID"})
private int specialNameLabId;
private MonsterSpecialNameData specialNameData;
} }

View File

@ -21,6 +21,8 @@ import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.GameMainQuest; import emu.grasscutter.game.quest.GameMainQuest;
import emu.grasscutter.game.world.SceneGroupInstance;
import java.util.List; import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -449,4 +451,14 @@ public final class DatabaseHelper {
public static void saveAchievementData(Achievements achievements) { public static void saveAchievementData(Achievements achievements) {
DatabaseManager.getGameDatastore().save(achievements); DatabaseManager.getGameDatastore().save(achievements);
} }
public static void saveGroupInstance(SceneGroupInstance instance) {
DatabaseManager.getGameDatastore().save(instance);
}
public static SceneGroupInstance loadGroupInstance(int groupId, Player owner) {
return DatabaseManager.getGameDatastore().find(SceneGroupInstance.class)
.filter(Filters.and(Filters.eq("ownerUid", owner.getUid()),
Filters.eq("groupId", groupId))).first();
}
} }

View File

@ -103,6 +103,13 @@ public class PlayerActivityData {
int curProgress; int curProgress;
boolean isTakenReward; boolean isTakenReward;
/**
* @return True when the progress of this watcher has reached the total progress.
*/
public boolean isFinished(){
return this.curProgress >= this.totalProgress;
}
public static WatcherInfo init(ActivityWatcher watcher) { public static WatcherInfo init(ActivityWatcher watcher) {
return WatcherInfo.of() return WatcherInfo.of()
.watcherId(watcher.getWatcherId()) .watcherId(watcher.getWatcherId())

View File

@ -49,6 +49,8 @@ import lombok.Setter;
import lombok.val; import lombok.val;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import javax.annotation.Nonnull;
@Entity(value = "avatars", useDiscriminator = false) @Entity(value = "avatars", useDiscriminator = false)
public class Avatar { public class Avatar {
@Transient @Getter private final Int2ObjectMap<GameItem> equips; @Transient @Getter private final Int2ObjectMap<GameItem> equips;
@ -234,7 +236,23 @@ public class Avatar {
this.skillDepot = skillDepot; // Used while loading this from the database this.skillDepot = skillDepot; // Used while loading this from the database
} }
/**
* Changes this avatar's skill depot.
* Does not notify the player of the change.
*
* @param skillDepot The new skill depot.
*/
public void setSkillDepotData(AvatarSkillDepotData skillDepot) { public void setSkillDepotData(AvatarSkillDepotData skillDepot) {
this.setSkillDepotData(skillDepot, false);
}
/**
* Changes this avatar's skill depot.
*
* @param skillDepot The new skill depot.
* @param notify Whether to notify the player of the change.
*/
public void setSkillDepotData(AvatarSkillDepotData skillDepot, boolean notify) {
// Set id and depot // Set id and depot
this.skillDepotId = skillDepot.getId(); this.skillDepotId = skillDepot.getId();
this.skillDepot = skillDepot; this.skillDepot = skillDepot;
@ -251,6 +269,38 @@ public class Avatar {
.filter(proudSkillId -> GameData.getProudSkillDataMap().containsKey(proudSkillId)) .filter(proudSkillId -> GameData.getProudSkillDataMap().containsKey(proudSkillId))
.forEach(proudSkillId -> this.proudSkillList.add(proudSkillId)); .forEach(proudSkillId -> this.proudSkillList.add(proudSkillId));
this.recalcStats(); this.recalcStats();
// Send the depot change notification.
if (notify) this.owner.sendPacket(new PacketAvatarSkillDepotChangeNotify(this));
}
/**
* Changes the avatar's element to the target element, if the character has values for it set in the candSkillDepot
*
* @param elementTypeToChange element to change to
* @return false if failed or already using that element, true if it actually changed
*/
public boolean changeElement(@Nonnull ElementType elementTypeToChange) {
var candSkillDepotIdsList = this.avatarData.getCandSkillDepotIds();
var candSkillDepotIndex = elementTypeToChange.getDepotIndex();
// if no candidate skill to change or index out of bound
if (candSkillDepotIdsList == null ||
candSkillDepotIndex >= candSkillDepotIdsList.size()) {
return false;
}
var candSkillDepotId = candSkillDepotIdsList.get(candSkillDepotIndex);
// Sanity checks for skill depots
val skillDepot = GameData.getAvatarSkillDepotDataMap().get(candSkillDepotId);
if (skillDepot == null || skillDepot.getId() == skillDepotId) {
return false;
}
// Set skill depot
setSkillDepotData(skillDepot, true);
return true;
} }
public List<Integer> getFetterList() { public List<Integer> getFetterList() {

View File

@ -1,14 +1,18 @@
package emu.grasscutter.game.dungeons; package emu.grasscutter.game.dungeons;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.dungeons.dungeon_results.BaseDungeonResult;
import emu.grasscutter.server.packet.send.PacketDungeonSettleNotify; import emu.grasscutter.server.packet.send.PacketDungeonSettleNotify;
import emu.grasscutter.utils.Utils;
public class BasicDungeonSettleListener implements DungeonSettleListener { public class BasicDungeonSettleListener implements DungeonSettleListener {
@Override @Override
public void onDungeonSettle(Scene scene) { public void onDungeonSettle(DungeonManager dungeonManager, BaseDungeonResult.DungeonEndReason endReason) {
scene.setAutoCloseTime(Utils.getCurrentSeconds() + 1000); var scene = dungeonManager.getScene();
scene.broadcastPacket(new PacketDungeonSettleNotify(scene.getChallenge())); var dungeonData = dungeonManager.getDungeonData();
var time = scene.getSceneTimeSeconds() - dungeonManager.getStartSceneTime() ;
// TODO time taken and chests handling
DungeonEndStats stats = new DungeonEndStats(scene.getKilledMonsterCount(), time, 0, endReason);
scene.broadcastPacket(new PacketDungeonSettleNotify(new BaseDungeonResult(dungeonData, stats)));
} }
} }

View File

@ -24,20 +24,22 @@ import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.annotation.Nullable;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import lombok.val; import lombok.val;
/** import javax.annotation.Nullable;
* TODO handle time limits TODO handle respawn points TODO handle team wipes and respawns TODO check import java.util.*;
* monster level and levelConfigMap import java.util.stream.Collectors;
*/ import java.util.stream.IntStream;
public class DungeonManager {
/**
* TODO handle time limits
* TODO handle respawn points
* TODO handle team wipes and respawns
* TODO check monster level and levelConfigMap
*/
public final class DungeonManager {
@Getter private final Scene scene; @Getter private final Scene scene;
@Getter private final DungeonData dungeonData; @Getter private final DungeonData dungeonData;
@Getter private final DungeonPassConfigData passConfigData; @Getter private final DungeonPassConfigData passConfigData;
@ -69,12 +71,14 @@ public class DungeonManager {
if (getScene().getWorld().getServer().getDungeonSystem().triggerCondition(cond, params)) { if (getScene().getWorld().getServer().getDungeonSystem().triggerCondition(cond, params)) {
finishedConditions[i] = 1; finishedConditions[i] = 1;
} }
} }
} }
if (isFinishedSuccessfully()) { if (isFinishedSuccessfully()) {
finishDungeon(); finishDungeon();
} }
} }
public boolean isFinishedSuccessfully() { public boolean isFinishedSuccessfully() {
@ -94,20 +98,19 @@ public class DungeonManager {
return false; return false;
} }
scene.broadcastPacket( scene.broadcastPacket(new PacketDungeonWayPointNotify(activeDungeonWayPoints.add(pointId), activeDungeonWayPoints));
new PacketDungeonWayPointNotify(
activeDungeonWayPoints.add(pointId), activeDungeonWayPoints));
newestWayPoint = pointId; newestWayPoint = pointId;
Grasscutter.getLogger().debug("[unimplemented respawn] activated respawn point {}", pointId); Grasscutter.getLogger().debug("[unimplemented respawn] activated respawn point {}", pointId);
return true; return true;
} }
@Nullable public Position getRespawnLocation() { @Nullable
public Position getRespawnLocation() {
if (newestWayPoint == 0) { // validity is checked before setting it, so if != 0 its always valid if (newestWayPoint == 0) { // validity is checked before setting it, so if != 0 its always valid
return null; return null;
} }
val pointData = GameData.getScenePointEntryById(scene.getId(), newestWayPoint).getPointData(); var pointData = GameData.getScenePointEntryById(scene.getId(), newestWayPoint).getPointData();
return pointData.getTranPos() != null ? pointData.getTranPos() : pointData.getPos(); return pointData.getTranPos() != null ? pointData.getTranPos() : pointData.getPos();
} }
@ -120,9 +123,7 @@ public class DungeonManager {
} }
public boolean getStatueDrops(Player player, boolean useCondensed, int groupId) { public boolean getStatueDrops(Player player, boolean useCondensed, int groupId) {
if (!isFinishedSuccessfully() if (!isFinishedSuccessfully() || dungeonData.getRewardPreviewData() == null || dungeonData.getRewardPreviewData().getPreviewItems().length == 0) {
|| dungeonData.getRewardPreviewData() == null
|| dungeonData.getRewardPreviewData().getPreviewItems().length == 0) {
return false; return false;
} }
@ -131,6 +132,7 @@ public class DungeonManager {
return false; return false;
} }
if (!handleCost(player, useCondensed)) { if (!handleCost(player, useCondensed)) {
return false; return false;
} }
@ -196,10 +198,8 @@ public class DungeonManager {
// Roll items for this group. // Roll items for this group.
// Here, we have to handle stacking, or the client will not display results correctly. // Here, we have to handle stacking, or the client will not display results correctly.
// For now, we use the following logic: If the possible drop item are a list of multiple // For now, we use the following logic: If the possible drop item are a list of multiple items,
// items, // we roll them separately. If not, we stack them. This should work out in practice, at least
// we roll them separately. If not, we stack them. This should work out in practice, at
// least
// for the currently existing set of dungeons. // for the currently existing set of dungeons.
if (entry.getItems().size() == 1) { if (entry.getItems().size() == 1) {
rewards.add(new GameItem(entry.getItems().get(0), amount)); rewards.add(new GameItem(entry.getItems().get(0), amount));
@ -207,8 +207,7 @@ public class DungeonManager {
for (int i = 0; i < amount; i++) { for (int i = 0; i < amount; i++) {
// int itemIndex = ThreadLocalRandom.current().nextInt(0, entry.getItems().size()); // int itemIndex = ThreadLocalRandom.current().nextInt(0, entry.getItems().size());
// int itemId = entry.getItems().get(itemIndex); // int itemId = entry.getItems().get(itemIndex);
int itemId = int itemId = Utils.drawRandomListElement(entry.getItems(), entry.getItemProbabilities());
Utils.drawRandomListElement(entry.getItems(), entry.getItemProbabilities());
rewards.add(new GameItem(itemId, 1)); rewards.add(new GameItem(itemId, 1));
} }
} }
@ -216,8 +215,7 @@ public class DungeonManager {
} }
// Otherwise, we fall back to the preview data. // Otherwise, we fall back to the preview data.
else { else {
Grasscutter.getLogger() Grasscutter.getLogger().info("No drop data found or dungeon {}, falling back to preview data ...", dungeonId);
.info("No drop data found or dungeon {}, falling back to preview data ...", dungeonId);
for (ItemParamData param : dungeonData.getRewardPreviewData().getPreviewItems()) { for (ItemParamData param : dungeonData.getRewardPreviewData().getPreviewItems()) {
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1))); rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
} }
@ -234,40 +232,32 @@ public class DungeonManager {
case DUNGEON_ACTIVITY -> { case DUNGEON_ACTIVITY -> {
switch (getDungeonData().getPlayType()) { switch (getDungeonData().getPlayType()) {
case DUNGEON_PLAY_TYPE_TRIAL_AVATAR -> { case DUNGEON_PLAY_TYPE_TRIAL_AVATAR -> {
val activityHandler = val activityHandler = player.getActivityManager()
player .getActivityHandlerAs(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR, TrialAvatarActivityHandler.class);
.getActivityManager() activityHandler.ifPresent(trialAvatarActivityHandler ->
.getActivityHandlerAs(
ActivityType.NEW_ACTIVITY_TRIAL_AVATAR, TrialAvatarActivityHandler.class);
activityHandler.ifPresent(
trialAvatarActivityHandler ->
this.trialTeam = trialAvatarActivityHandler.getTrialAvatarDungeonTeam()); this.trialTeam = trialAvatarActivityHandler.getTrialAvatarDungeonTeam());
} }
} }
} }
case DUNGEON_ELEMENT_CHALLENGE -> {} // TODO case DUNGEON_ELEMENT_CHALLENGE -> {} // TODO
} }
if (this.trialTeam != null) { if (this.trialTeam != null) {
player.getTeamManager().addTrialAvatars(trialTeam.trialAvatarIds); player.getTeamManager().addTrialAvatars(trialTeam.trialAvatarIds);
} }
} }
public void unsetTrialTeam(Player player){ public void unsetTrialTeam(Player player){
if (this.trialTeam == null) { if (this.trialTeam == null) return;
return;
}
player.getTeamManager().removeTrialAvatar(); player.getTeamManager().removeTrialAvatar();
this.trialTeam = null; this.trialTeam = null;
} }
public void startDungeon() { public void startDungeon() {
this.startSceneTime = scene.getSceneTimeSeconds(); this.startSceneTime = scene.getSceneTimeSeconds();
scene scene.getPlayers().forEach(p -> {
.getPlayers() p.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_ENTER_DUNGEON, dungeonData.getId());
.forEach(
p -> {
p.getQuestManager()
.queueEvent(QuestContent.QUEST_CONTENT_ENTER_DUNGEON, dungeonData.getId());
applyTrialTeam(p); applyTrialTeam(p);
}); });
} }
@ -278,16 +268,10 @@ public class DungeonManager {
} }
public void notifyEndDungeon(boolean successfully) { public void notifyEndDungeon(boolean successfully) {
scene scene.getPlayers().forEach(p -> {
.getPlayers()
.forEach(
p -> {
// Quest trigger // Quest trigger
p.getQuestManager() p.getQuestManager().queueEvent(successfully ?
.queueEvent( QuestContent.QUEST_CONTENT_FINISH_DUNGEON : QuestContent.QUEST_CONTENT_FAIL_DUNGEON,
successfully
? QuestContent.QUEST_CONTENT_FINISH_DUNGEON
: QuestContent.QUEST_CONTENT_FAIL_DUNGEON,
dungeonData.getId()); dungeonData.getId());
// Battle pass trigger // Battle pass trigger
@ -295,9 +279,7 @@ public class DungeonManager {
p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_FINISH_DUNGEON); p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_FINISH_DUNGEON);
} }
}); });
scene scene.getScriptManager().callEvent(new ScriptArgs(0, EventType.EVENT_DUNGEON_SETTLE, successfully ? 1 : 0));
.getScriptManager()
.callEvent(new ScriptArgs(0, EventType.EVENT_DUNGEON_SETTLE, successfully ? 1 : 0));
} }
public void quitDungeon() { public void quitDungeon() {

View File

@ -1,7 +1,7 @@
package emu.grasscutter.game.dungeons; package emu.grasscutter.game.dungeons;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.dungeons.dungeon_results.BaseDungeonResult;
public interface DungeonSettleListener { public interface DungeonSettleListener {
void onDungeonSettle(Scene scene); void onDungeonSettle(DungeonManager dungeonManager, BaseDungeonResult.DungeonEndReason endReason);
} }

View File

@ -3,27 +3,63 @@ package emu.grasscutter.game.dungeons;
import emu.grasscutter.GameConstants; import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.ScenePointEntry;
import emu.grasscutter.data.excels.dungeon.DungeonData;
import emu.grasscutter.data.excels.dungeon.DungeonPassConfigData;
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.SceneType; import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.server.game.BaseGameSystem; import emu.grasscutter.server.game.BaseGameSystem;
import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.PacketDungeonEntryInfoRsp; import emu.grasscutter.server.packet.send.PacketDungeonEntryInfoRsp;
import emu.grasscutter.server.packet.send.PacketPlayerEnterDungeonRsp;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.val;
import org.reflections.Reflections;
import java.util.List; import java.util.List;
public final class DungeonSystem extends BaseGameSystem { public class DungeonSystem extends BaseGameSystem {
private static final BasicDungeonSettleListener basicDungeonSettleObserver = private static final BasicDungeonSettleListener basicDungeonSettleObserver = new BasicDungeonSettleListener();
new BasicDungeonSettleListener(); private final Int2ObjectMap<DungeonBaseHandler> passCondHandlers;
public DungeonSystem(GameServer server) { public DungeonSystem(GameServer server) {
super(server); super(server);
this.passCondHandlers = new Int2ObjectOpenHashMap<>();
registerHandlers();
}
public void registerHandlers() {
this.registerHandlers(this.passCondHandlers, "emu.grasscutter.game.dungeons.pass_condition", DungeonBaseHandler.class);
}
public <T> void registerHandlers(Int2ObjectMap<T> map, String packageName, Class<T> clazz) {
Reflections reflections = new Reflections(packageName);
var handlerClasses = reflections.getSubTypesOf(clazz);
for (var obj : handlerClasses) {
this.registerPacketHandler(map, obj);
}
}
public <T> void registerPacketHandler(Int2ObjectMap<T> map, Class<? extends T> handlerClass) {
try {
DungeonValue opcode = handlerClass.getAnnotation(DungeonValue.class);
if (opcode == null || opcode.value() == null) {
return;
}
map.put(opcode.value().ordinal(), handlerClass.getDeclaredConstructor().newInstance());
} catch (Exception e) {
e.printStackTrace();
}
} }
public void getEntryInfo(Player player, int pointId) { public void getEntryInfo(Player player, int pointId) {
var entry = GameData.getScenePointEntryById(player.getScene().getId(), pointId); ScenePointEntry entry = GameData.getScenePointEntryById(player.getScene().getId(), pointId);
if (entry == null) { if (entry == null) {
// Error // Error
@ -34,82 +70,88 @@ public final class DungeonSystem extends BaseGameSystem {
player.sendPacket(new PacketDungeonEntryInfoRsp(player, entry.getPointData())); player.sendPacket(new PacketDungeonEntryInfoRsp(player, entry.getPointData()));
} }
public boolean enterDungeon(Player player, int pointId, int dungeonId) { public boolean triggerCondition(DungeonPassConfigData.DungeonPassCondition condition, int... params) {
var data = GameData.getDungeonDataMap().get(dungeonId); var handler = passCondHandlers.get(condition.getCondType().ordinal());
if (data == null) {
if (handler == null) {
Grasscutter.getLogger().debug("Could not trigger condition {} at {}", condition.getCondType(), params);
return false; return false;
} }
Grasscutter.getLogger() return handler.execute(condition, params);
.debug(
"{}({}) is trying to enter dungeon {}",
player.getNickname(),
player.getUid(),
dungeonId);
var sceneId = data.getSceneId();
player.getScene().setPrevScene(sceneId);
if (player.getWorld().transferPlayerToScene(player, sceneId, data)) {
player.getScene().addDungeonSettleObserver(basicDungeonSettleObserver);
player.getQuestManager().triggerEvent(QuestContent.QUEST_CONTENT_ENTER_DUNGEON, data.getId());
} }
player.getScene().setPrevScenePoint(pointId); public boolean enterDungeon(Player player, int pointId, int dungeonId) {
player.sendPacket(new PacketPlayerEnterDungeonRsp(pointId, dungeonId)); DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
if (data == null) {
return false;
}
Grasscutter.getLogger().info("{}({}) is trying to enter dungeon {}" ,player.getNickname(),player.getUid(),dungeonId);
int sceneId = data.getSceneId();
var scene = player.getScene();
scene.setPrevScene(sceneId);
if (player.getWorld().transferPlayerToScene(player, sceneId, data)) {
scene = player.getScene();
scene.addDungeonSettleObserver(basicDungeonSettleObserver);
}
scene.setPrevScenePoint(pointId);
return true; return true;
} }
/** used in tower dungeons handoff */ /**
public boolean handoffDungeon( * used in tower dungeons handoff
Player player, int dungeonId, List<DungeonSettleListener> dungeonSettleListeners) { */
var data = GameData.getDungeonDataMap().get(dungeonId); public boolean handoffDungeon(Player player, int dungeonId, List<DungeonSettleListener> dungeonSettleListeners) {
DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
if (data == null) { if (data == null) {
return false; return false;
} }
Grasscutter.getLogger().info("{}({}) is trying to enter tower dungeon {}" ,player.getNickname(),player.getUid(),dungeonId);
Grasscutter.getLogger()
.debug(
"{}({}) is trying to enter tower dungeon {}",
player.getNickname(),
player.getUid(),
dungeonId);
if (player.getWorld().transferPlayerToScene(player, data.getSceneId(), data)) { if (player.getWorld().transferPlayerToScene(player, data.getSceneId(), data)) {
dungeonSettleListeners.forEach(player.getScene()::addDungeonSettleObserver); dungeonSettleListeners.forEach(player.getScene()::addDungeonSettleObserver);
} }
return true; return true;
} }
public void exitDungeon(Player player) { public void exitDungeon(Player player) {
var scene = player.getScene(); Scene scene = player.getScene();
if (scene==null || scene.getSceneType() != SceneType.SCENE_DUNGEON) { if (scene==null || scene.getSceneType() != SceneType.SCENE_DUNGEON) {
return; return;
} }
// Get previous scene // Get previous scene
var prevScene = scene.getPrevScene() > 0 ? scene.getPrevScene() : 3; int prevScene = scene.getPrevScene() > 0 ? scene.getPrevScene() : 3;
// Get previous position // Get previous position
var dungeonData = scene.getDungeonData(); val dungeonManager = scene.getDungeonManager();
var prevPos = new Position(GameConstants.START_POSITION); DungeonData dungeonData = dungeonManager != null ? dungeonManager.getDungeonData() : null;
Position prevPos = new Position(GameConstants.START_POSITION);
if (dungeonData != null) { if (dungeonData != null) {
var entry = GameData.getScenePointEntryById(prevScene, scene.getPrevScenePoint()); ScenePointEntry entry = GameData.getScenePointEntryById(prevScene, scene.getPrevScenePoint());
if (entry != null) { if (entry != null) {
prevPos.set(entry.getPointData().getTranPos()); prevPos.set(entry.getPointData().getTranPos());
} }
if(!dungeonManager.isFinishedSuccessfully()){
dungeonManager.quitDungeon();
} }
dungeonManager.unsetTrialTeam(player);
}
// clean temp team if it has // clean temp team if it has
player.getTeamManager().cleanTemporaryTeam(); player.getTeamManager().cleanTemporaryTeam();
player.getTowerManager().clearEntry(); player.getTowerManager().clearEntry();
// Transfer player back to world // Transfer player back to world
player.getWorld().transferPlayerToScene(player, prevScene, prevPos); player.getWorld().transferPlayerToScene(player, prevScene, prevPos);
player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp));
} }
} }

View File

@ -1,31 +1,38 @@
package emu.grasscutter.game.dungeons; package emu.grasscutter.game.dungeons;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.dungeons.dungeon_results.BaseDungeonResult.DungeonEndReason;
import emu.grasscutter.game.world.SceneGroupInstance;
import emu.grasscutter.game.dungeons.dungeon_results.TowerResult;
import emu.grasscutter.server.packet.send.PacketDungeonSettleNotify; import emu.grasscutter.server.packet.send.PacketDungeonSettleNotify;
import emu.grasscutter.server.packet.send.PacketTowerFloorRecordChangeNotify; import emu.grasscutter.server.packet.send.PacketTowerFloorRecordChangeNotify;
import emu.grasscutter.utils.Utils;
public class TowerDungeonSettleListener implements DungeonSettleListener { public class TowerDungeonSettleListener implements DungeonSettleListener {
@Override @Override
public void onDungeonSettle(Scene scene) { public void onDungeonSettle(DungeonManager dungeonManager, DungeonEndReason endReason) {
if (scene.getScriptManager().getVariables().containsKey("stage") var scene = dungeonManager.getScene();
&& scene.getScriptManager().getVariables().get("stage") == 1) { var dungeonData = dungeonManager.getDungeonData();
if (scene.getLoadedGroups().stream().anyMatch(g -> {
var variables = scene.getScriptManager().getVariables(g.id);
return variables != null && variables.containsKey("stage") && variables.get("stage") == 1;
})) {
return; return;
} }
scene.setAutoCloseTime(Utils.getCurrentSeconds() + 1000);
var towerManager = scene.getPlayers().get(0).getTowerManager(); var towerManager = scene.getPlayers().get(0).getTowerManager();
towerManager.notifyCurLevelRecordChangeWhenDone(3); towerManager.notifyCurLevelRecordChangeWhenDone(3);
scene.broadcastPacket( scene.broadcastPacket(new PacketTowerFloorRecordChangeNotify(
new PacketTowerFloorRecordChangeNotify( towerManager.getCurrentFloorId(),
towerManager.getCurrentFloorId(), 3, towerManager.canEnterScheduleFloor())); 3,
towerManager.canEnterScheduleFloor()
));
var challenge = scene.getChallenge();
var dungeonStats = new DungeonEndStats(scene.getKilledMonsterCount(), challenge.getFinishedTime(), 0, endReason);
var result = new TowerResult(dungeonData, dungeonStats, towerManager, challenge);
scene.broadcastPacket(new PacketDungeonSettleNotify(result));
scene.broadcastPacket(
new PacketDungeonSettleNotify(
scene.getChallenge(),
towerManager.hasNextFloor(),
towerManager.hasNextLevel(),
towerManager.hasNextLevel() ? 0 : towerManager.getNextFloorId()));
} }
} }

View File

@ -2,11 +2,14 @@ package emu.grasscutter.game.dungeons.challenge;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger; import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.SceneTrigger;
import emu.grasscutter.scripts.data.ScriptArgs; import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeBeginNotify; import emu.grasscutter.server.packet.send.PacketDungeonChallengeBeginNotify;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify; import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
@ -14,6 +17,7 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.val;
@Getter @Getter
@Setter @Setter
@ -78,26 +82,33 @@ public class WorldChallenge {
} }
public void done() { public void done() {
if (!inProgress()) { if (!this.inProgress()) return;
return; this.finish(true);
}
finish(true);
this.getScene()
.getScriptManager()
.callEvent(
EventType.EVENT_CHALLENGE_SUCCESS,
// TODO record the time in PARAM2 and used in action
new ScriptArgs().setParam2(finishedTime));
challengeTriggers.forEach(t -> t.onFinish(this)); var scene = this.getScene();
var dungeonManager = scene.getDungeonManager();
if (dungeonManager != null && dungeonManager.getDungeonData() != null) {
scene.getPlayers().forEach(p -> p.getActivityManager().triggerWatcher(
WatcherTriggerType.TRIGGER_FINISH_CHALLENGE,
String.valueOf(dungeonManager.getDungeonData().getId()),
String.valueOf(this.getGroup().id),
String.valueOf(this.getChallengeId())
));
}
scene.getScriptManager().callEvent(
// TODO record the time in PARAM2 and used in action
new ScriptArgs(this.getGroup().id, EventType.EVENT_CHALLENGE_SUCCESS).setParam2(finishedTime));
this.getScene().triggerDungeonEvent(DungeonPassConditionType.DUNGEON_COND_FINISH_CHALLENGE, getChallengeId(), getChallengeIndex());
this.challengeTriggers.forEach(t -> t.onFinish(this));
} }
public void fail(){ public void fail(){
if (!inProgress()) { if (!this.inProgress()) return;
return; this.finish(true);
}
finish(false); this.getScene().getScriptManager().callEvent(new ScriptArgs(this.getGroup().id, EventType.EVENT_CHALLENGE_FAIL));
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_FAIL, null);
challengeTriggers.forEach(t -> t.onFinish(this)); challengeTriggers.forEach(t -> t.onFinish(this));
} }
@ -132,6 +143,18 @@ public class WorldChallenge {
this.challengeTriggers.forEach(t -> t.onGadgetDeath(this, gadget)); this.challengeTriggers.forEach(t -> t.onGadgetDeath(this, gadget));
} }
public void onGroupTriggerDeath(SceneTrigger trigger) {
if(!this.inProgress()) return;
var triggerGroup = trigger.getCurrentGroup();
if (triggerGroup == null ||
triggerGroup.id != getGroup().id) {
return;
}
this.challengeTriggers.forEach(t -> t.onGroupTrigger(this, trigger));
}
public void onGadgetDamage(EntityGadget gadget) { public void onGadgetDamage(EntityGadget gadget) {
if (!inProgress()) { if (!inProgress()) {
return; return;

View File

@ -1,36 +1,35 @@
package emu.grasscutter.game.dungeons.challenge.factory; package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.data.GameData;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge; import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneGroup;
import lombok.val;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class ChallengeFactory { public abstract class ChallengeFactory {
private static final List<ChallengeFactoryHandler> challengeFactoryHandlers = new ArrayList<>(); private static final List<ChallengeFactoryHandler> challengeFactoryHandlers = new ArrayList<>();
static { static {
challengeFactoryHandlers.add(new DungeonChallengeFactoryHandler()); challengeFactoryHandlers.add(new KillAndGuardChallengeFactoryHandler());
challengeFactoryHandlers.add(new DungeonGuardChallengeFactoryHandler()); challengeFactoryHandlers.add(new KillMonsterCountChallengeFactoryHandler());
challengeFactoryHandlers.add(new KillGadgetChallengeFactoryHandler()); challengeFactoryHandlers.add(new KillMonsterInTimeChallengeFactoryHandler());
challengeFactoryHandlers.add(new KillMonsterChallengeFactoryHandler()); challengeFactoryHandlers.add(new KillMonsterTimeChallengeFactoryHandler());
challengeFactoryHandlers.add(new SurviveChallengeFactoryHandler());
challengeFactoryHandlers.add(new TriggerInTimeChallengeFactoryHandler());
} }
public static WorldChallenge getChallenge( public static WorldChallenge getChallenge(int localChallengeId, int challengeDataId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group){
int param1, val challengeData = GameData.getDungeonChallengeConfigDataMap().get(challengeDataId);
int param2, val challengeType = challengeData.getChallengeType();
int param3,
int param4,
int param5,
int param6,
Scene scene,
SceneGroup group) {
for(var handler : challengeFactoryHandlers){ for(var handler : challengeFactoryHandlers){
if (!handler.isThisType(param1, param2, param3, param4, param5, param6, scene, group)) { if(!handler.isThisType(challengeType)){
continue; continue;
} }
return handler.build(param1, param2, param3, param4, param5, param6, scene, group); return handler.build(localChallengeId, challengeDataId, param3, param4, param5, param6, scene, group);
} }
return null; return null;
} }

View File

@ -1,27 +1,11 @@
package emu.grasscutter.game.dungeons.challenge.factory; package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge; import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneGroup;
public interface ChallengeFactoryHandler { public interface ChallengeFactoryHandler {
boolean isThisType( boolean isThisType(ChallengeType challengeType);
int challengeIndex, WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group);
int challengeId,
int param3,
int param4,
int param5,
int param6,
Scene scene,
SceneGroup group);
WorldChallenge build(
int challengeIndex,
int challengeId,
int param3,
int param4,
int param5,
int param6,
Scene scene,
SceneGroup group);
} }

View File

@ -1,48 +0,0 @@
package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List;
public class DungeonChallengeFactoryHandler implements ChallengeFactoryHandler {
@Override
public boolean isThisType(
int challengeIndex,
int challengeId,
int param3,
int param4,
int param5,
int param6,
Scene scene,
SceneGroup group) {
// ActiveChallenge with 1,1000,300,233101003,15,0
return scene.getSceneType() == SceneType.SCENE_DUNGEON && param4 == group.id;
}
@Override
public WorldChallenge build(
int challengeIndex,
int challengeId,
int param3,
int param4,
int param5,
int param6,
Scene scene,
SceneGroup group) {
var realGroup = scene.getScriptManager().getGroupById(param4);
return new DungeonChallenge(
scene,
realGroup,
challengeId, // Id
challengeIndex, // Index
List.of(param5, param3),
param3, // Limit
param5, // Goal
List.of(new InTimeTrigger(), new KillMonsterTrigger()));
}
}

View File

@ -1,47 +0,0 @@
package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.challenge.trigger.GuardTrigger;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List;
public class DungeonGuardChallengeFactoryHandler implements ChallengeFactoryHandler {
@Override
public boolean isThisType(
int challengeIndex,
int challengeId,
int param3,
int param4,
int param5,
int param6,
Scene scene,
SceneGroup group) {
// ActiveChallenge with 1,188,234101003,12,3030,0
return scene.getSceneType() == SceneType.SCENE_DUNGEON && param3 == group.id;
}
@Override
public WorldChallenge build(
int challengeIndex,
int challengeId,
int param3,
int param4,
int param5,
int param6,
Scene scene,
SceneGroup group) {
var realGroup = scene.getScriptManager().getGroupById(param3);
return new DungeonChallenge(
scene,
realGroup,
challengeId, // Id
challengeIndex, // Index
List.of(param4, 0),
0, // Limit
param4, // Goal
List.of(new GuardTrigger()));
}
}

View File

@ -1,16 +1,17 @@
package emu.grasscutter.game.dungeons.challenge.factory; package emu.grasscutter.game.dungeons.challenge.factory;
import static emu.grasscutter.game.dungeons.challenge.enums.ChallengeType.CHALLENGE_KILL_COUNT_GUARD_HP;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge; import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType; import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType;
import emu.grasscutter.game.dungeons.challenge.trigger.GuardTrigger; import emu.grasscutter.game.dungeons.challenge.trigger.GuardTrigger;
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterCountTrigger; import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterCountTrigger;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List;
import lombok.val; import lombok.val;
import java.util.List;
import static emu.grasscutter.game.dungeons.challenge.enums.ChallengeType.CHALLENGE_KILL_COUNT_GUARD_HP;
public class KillAndGuardChallengeFactoryHandler implements ChallengeFactoryHandler{ public class KillAndGuardChallengeFactoryHandler implements ChallengeFactoryHandler{
@Override @Override
public boolean isThisType(ChallengeType challengeType) { public boolean isThisType(ChallengeType challengeType) {
@ -19,19 +20,10 @@ public class KillAndGuardChallengeFactoryHandler implements ChallengeFactoryHand
} }
@Override /*TODO check param4 == monstesToKill*/ @Override /*TODO check param4 == monstesToKill*/
public WorldChallenge build( public WorldChallenge build(int challengeIndex, int challengeId, int groupId, int monstersToKill, int gadgetCFGId, int unused, Scene scene, SceneGroup group) {
int challengeIndex,
int challengeId,
int groupId,
int monstersToKill,
int gadgetCFGId,
int unused,
Scene scene,
SceneGroup group) {
val realGroup = scene.getScriptManager().getGroupById(groupId); val realGroup = scene.getScriptManager().getGroupById(groupId);
return new WorldChallenge( return new WorldChallenge(
scene, scene, realGroup,
realGroup,
challengeId, // Id challengeId, // Id
challengeIndex, // Index challengeIndex, // Index
List.of(monstersToKill, 0), List.of(monstersToKill, 0),

View File

@ -1,48 +0,0 @@
package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
import emu.grasscutter.game.dungeons.challenge.trigger.KillGadgetTrigger;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List;
public class KillGadgetChallengeFactoryHandler implements ChallengeFactoryHandler {
@Override
public boolean isThisType(
int challengeIndex,
int challengeId,
int param3,
int param4,
int param5,
int param6,
Scene scene,
SceneGroup group) {
// kill gadgets(explosive barrel) in time
// ActiveChallenge with 56,201,20,2,201,4
// open chest in time
// ActiveChallenge with 666,202,30,7,202,1
return challengeId == 201 || challengeId == 202;
}
@Override
public WorldChallenge build(
int challengeIndex,
int challengeId,
int param3,
int param4,
int param5,
int param6,
Scene scene,
SceneGroup group) {
return new WorldChallenge(
scene,
group,
challengeId, // Id
challengeIndex, // Index
List.of(param3, param6, 0),
param3, // Limit
param6, // Goal
List.of(new InTimeTrigger(), new KillGadgetTrigger()));
}
}

View File

@ -1,46 +0,0 @@
package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List;
public class KillMonsterChallengeFactoryHandler implements ChallengeFactoryHandler {
@Override
public boolean isThisType(
int challengeIndex,
int challengeId,
int param3,
int param4,
int param5,
int param6,
Scene scene,
SceneGroup group) {
// ActiveChallenge with 180,180,45,133108061,1,0
return challengeId == 180;
}
@Override
public WorldChallenge build(
int challengeIndex,
int challengeId,
int param3,
int param4,
int param5,
int param6,
Scene scene,
SceneGroup group) {
var realGroup = scene.getScriptManager().getGroupById(param4);
return new WorldChallenge(
scene,
realGroup,
challengeId, // Id
challengeIndex, // Index
List.of(param5, param3),
param3, // Limit
param5, // Goal
List.of(new KillMonsterTrigger(), new InTimeTrigger()));
}
}

View File

@ -5,9 +5,10 @@ import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType;
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterCountTrigger; import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterCountTrigger;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List;
import lombok.val; import lombok.val;
import java.util.List;
public class KillMonsterCountChallengeFactoryHandler implements ChallengeFactoryHandler{ public class KillMonsterCountChallengeFactoryHandler implements ChallengeFactoryHandler{
@Override @Override
public boolean isThisType(ChallengeType challengeType) { public boolean isThisType(ChallengeType challengeType) {
@ -16,24 +17,16 @@ public class KillMonsterCountChallengeFactoryHandler implements ChallengeFactory
} }
@Override @Override
public WorldChallenge build( public WorldChallenge build(int challengeIndex, int challengeId, int groupId, int goal, int param5, int param6, Scene scene, SceneGroup group) {
int challengeIndex,
int challengeId,
int groupId,
int goal,
int param5,
int param6,
Scene scene,
SceneGroup group) {
val realGroup = scene.getScriptManager().getGroupById(groupId); val realGroup = scene.getScriptManager().getGroupById(groupId);
return new WorldChallenge( return new WorldChallenge(
scene, scene, realGroup,
realGroup,
challengeId, // Id challengeId, // Id
challengeIndex, // Index challengeIndex, // Index
List.of(goal, groupId), List.of(goal, groupId),
0, // Limit 0, // Limit
goal, // Goal goal, // Goal
List.of(new KillMonsterCountTrigger())); List.of(new KillMonsterCountTrigger())
);
} }
} }

View File

@ -6,9 +6,10 @@ import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger; import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List;
import lombok.val; import lombok.val;
import java.util.List;
public class KillMonsterInTimeChallengeFactoryHandler implements ChallengeFactoryHandler{ public class KillMonsterInTimeChallengeFactoryHandler implements ChallengeFactoryHandler{
@Override @Override
public boolean isThisType(ChallengeType challengeType) { public boolean isThisType(ChallengeType challengeType) {
@ -17,24 +18,16 @@ public class KillMonsterInTimeChallengeFactoryHandler implements ChallengeFactor
} }
@Override @Override
public WorldChallenge build( public WorldChallenge build(int challengeIndex, int challengeId, int timeLimit, int groupId, int targetCfgId, int param6, Scene scene, SceneGroup group) {
int challengeIndex,
int challengeId,
int timeLimit,
int groupId,
int targetCfgId,
int param6,
Scene scene,
SceneGroup group) {
val realGroup = scene.getScriptManager().getGroupById(groupId); val realGroup = scene.getScriptManager().getGroupById(groupId);
return new WorldChallenge( return new WorldChallenge(
scene, scene, realGroup,
realGroup,
challengeId, // Id challengeId, // Id
challengeIndex, // Index challengeIndex, // Index
List.of(timeLimit), List.of(timeLimit),
timeLimit, // Limit timeLimit, // Limit
0, // Goal 0, // Goal
List.of(new KillMonsterTrigger(targetCfgId), new InTimeTrigger())); List.of(new KillMonsterTrigger(targetCfgId), new InTimeTrigger())
);
} }
} }

View File

@ -6,37 +6,30 @@ import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterCountTrigger; import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterCountTrigger;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List;
import lombok.val; import lombok.val;
import java.util.List;
public class KillMonsterTimeChallengeFactoryHandler implements ChallengeFactoryHandler{ public class KillMonsterTimeChallengeFactoryHandler implements ChallengeFactoryHandler{
@Override @Override
public boolean isThisType(ChallengeType challengeType) { public boolean isThisType(ChallengeType challengeType) {
// ActiveChallenge with 180,180,45,133108061,1,0 // ActiveChallenge with 180,180,45,133108061,1,0
// ActiveChallenge Fast with 1001, 5, 15, 240004005, 10, 0 // ActiveChallenge Fast with 1001, 5, 15, 240004005, 10, 0
return challengeType == ChallengeType.CHALLENGE_KILL_COUNT_IN_TIME return challengeType == ChallengeType.CHALLENGE_KILL_COUNT_IN_TIME ||
|| challengeType == ChallengeType.CHALLENGE_KILL_COUNT_FAST; challengeType == ChallengeType.CHALLENGE_KILL_COUNT_FAST;
} }
@Override @Override
public WorldChallenge build( public WorldChallenge build(int challengeIndex, int challengeId, int timeLimit, int groupId, int targetCount, int param6, Scene scene, SceneGroup group) {
int challengeIndex,
int challengeId,
int timeLimit,
int groupId,
int targetCount,
int param6,
Scene scene,
SceneGroup group) {
val realGroup = scene.getScriptManager().getGroupById(groupId); val realGroup = scene.getScriptManager().getGroupById(groupId);
return new WorldChallenge( return new WorldChallenge(
scene, scene, realGroup,
realGroup,
challengeId, // Id challengeId, // Id
challengeIndex, // Index challengeIndex, // Index
List.of(targetCount, timeLimit), List.of(targetCount, timeLimit),
timeLimit, // Limit timeLimit, // Limit
targetCount, // Goal targetCount, // Goal
List.of(new KillMonsterCountTrigger(), new InTimeTrigger())); List.of(new KillMonsterCountTrigger(), new InTimeTrigger())
);
} }
} }

View File

@ -1,14 +1,15 @@
package emu.grasscutter.game.dungeons.challenge.factory; package emu.grasscutter.game.dungeons.challenge.factory;
import static emu.grasscutter.game.dungeons.challenge.enums.ChallengeType.CHALLENGE_SURVIVE;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge; import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType; import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType;
import emu.grasscutter.game.dungeons.challenge.trigger.ForTimeTrigger; import emu.grasscutter.game.dungeons.challenge.trigger.ForTimeTrigger;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List; import java.util.List;
import static emu.grasscutter.game.dungeons.challenge.enums.ChallengeType.CHALLENGE_SURVIVE;
public class SurviveChallengeFactoryHandler implements ChallengeFactoryHandler { public class SurviveChallengeFactoryHandler implements ChallengeFactoryHandler {
@Override @Override
public boolean isThisType(ChallengeType challengeType) { public boolean isThisType(ChallengeType challengeType) {
@ -18,23 +19,15 @@ public class SurviveChallengeFactoryHandler implements ChallengeFactoryHandler {
} }
@Override @Override
public WorldChallenge build( public WorldChallenge build(int challengeIndex, int challengeId, int timeToSurvive, int unused4, int unused5, int unused6, Scene scene, SceneGroup group) {
int challengeIndex,
int challengeId,
int timeToSurvive,
int unused4,
int unused5,
int unused6,
Scene scene,
SceneGroup group) {
return new WorldChallenge( return new WorldChallenge(
scene, scene, group,
group,
challengeId, // Id challengeId, // Id
challengeIndex, // Index challengeIndex, // Index
List.of(timeToSurvive), List.of(timeToSurvive),
timeToSurvive, // Limit timeToSurvive, // Limit
0, // Goal 0, // Goal
List.of(new ForTimeTrigger())); List.of(new ForTimeTrigger())
);
} }
} }

View File

@ -1,15 +1,16 @@
package emu.grasscutter.game.dungeons.challenge.factory; package emu.grasscutter.game.dungeons.challenge.factory;
import static emu.grasscutter.game.dungeons.challenge.enums.ChallengeType.CHALLENGE_TRIGGER_IN_TIME;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge; import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType; import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType;
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger; import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
import emu.grasscutter.game.dungeons.challenge.trigger.TriggerGroupTriggerTrigger; import emu.grasscutter.game.dungeons.challenge.trigger.TriggerGroupTriggerTrigger;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List; import java.util.List;
import static emu.grasscutter.game.dungeons.challenge.enums.ChallengeType.CHALLENGE_TRIGGER_IN_TIME;
public class TriggerInTimeChallengeFactoryHandler implements ChallengeFactoryHandler { public class TriggerInTimeChallengeFactoryHandler implements ChallengeFactoryHandler {
@Override @Override
public boolean isThisType(ChallengeType challengeType) { public boolean isThisType(ChallengeType challengeType) {
@ -21,23 +22,15 @@ public class TriggerInTimeChallengeFactoryHandler implements ChallengeFactoryHan
} }
@Override @Override
public WorldChallenge build( public WorldChallenge build(int challengeIndex, int challengeId, int timeLimit, int param4, int triggerTag, int triggerCount, Scene scene, SceneGroup group) {
int challengeIndex,
int challengeId,
int timeLimit,
int param4,
int triggerTag,
int triggerCount,
Scene scene,
SceneGroup group) {
return new WorldChallenge( return new WorldChallenge(
scene, scene, group,
group,
challengeId, // Id challengeId, // Id
challengeIndex, // Index challengeIndex, // Index
List.of(timeLimit, triggerCount), List.of(timeLimit, triggerCount),
timeLimit, // Limit timeLimit, // Limit
triggerCount, // Goal triggerCount, // Goal
List.of(new InTimeTrigger(), new TriggerGroupTriggerTrigger(Integer.toString(triggerTag)))); List.of(new InTimeTrigger(), new TriggerGroupTriggerTrigger(Integer.toString(triggerTag)))
);
} }
} }

View File

@ -3,17 +3,14 @@ package emu.grasscutter.game.dungeons.challenge.trigger;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge; import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.scripts.data.SceneTrigger;
public abstract class ChallengeTrigger { public abstract class ChallengeTrigger {
public void onBegin(WorldChallenge challenge) { } public void onBegin(WorldChallenge challenge) { }
public void onFinish(WorldChallenge challenge) { } public void onFinish(WorldChallenge challenge) { }
public void onMonsterDeath(WorldChallenge challenge, EntityMonster monster) { } public void onMonsterDeath(WorldChallenge challenge, EntityMonster monster) { }
public void onGadgetDeath(WorldChallenge challenge, EntityGadget gadget) { } public void onGadgetDeath(WorldChallenge challenge, EntityGadget gadget) { }
public void onCheckTimeout(WorldChallenge challenge) { } public void onCheckTimeout(WorldChallenge challenge) { }
public void onGadgetDamage(WorldChallenge challenge, EntityGadget gadget) { } public void onGadgetDamage(WorldChallenge challenge, EntityGadget gadget) { }
public void onGroupTrigger(WorldChallenge challenge, SceneTrigger trigger) { }
} }

View File

@ -2,22 +2,34 @@ package emu.grasscutter.game.dungeons.challenge.trigger;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge; import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify; import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
public class GuardTrigger extends KillMonsterTrigger { public class GuardTrigger extends ChallengeTrigger {
@Override private final int entityToProtectCFGId;
private int lastSendPercent = 100;
public GuardTrigger(int entityToProtectCFGId){
this.entityToProtectCFGId = entityToProtectCFGId;
}
public void onBegin(WorldChallenge challenge) { public void onBegin(WorldChallenge challenge) {
super.onBegin(challenge);
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, 100)); challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, 100));
} }
@Override @Override
public void onGadgetDamage(WorldChallenge challenge, EntityGadget gadget) { public void onGadgetDamage(WorldChallenge challenge, EntityGadget gadget) {
if(gadget.getConfigId() != entityToProtectCFGId){
return;
}
var curHp = gadget.getFightProperties().get(FightProperty.FIGHT_PROP_CUR_HP.getId()); var curHp = gadget.getFightProperties().get(FightProperty.FIGHT_PROP_CUR_HP.getId());
var maxHp = gadget.getFightProperties().get(FightProperty.FIGHT_PROP_BASE_HP.getId()); var maxHp = gadget.getFightProperties().get(FightProperty.FIGHT_PROP_BASE_HP.getId());
int percent = (int) (curHp / maxHp); int percent = (int) (curHp / maxHp);
if(percent!=lastSendPercent) {
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, percent)); challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, percent));
lastSendPercent = percent;
}
if(percent <= 0){ if(percent <= 0){
challenge.fail(); challenge.fail();

View File

@ -3,21 +3,19 @@ package emu.grasscutter.game.dungeons.challenge.trigger;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge; import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify; import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class KillMonsterTrigger extends ChallengeTrigger{ public class KillMonsterTrigger extends ChallengeTrigger{
private int monsterCfgId;
@Override @Override
public void onBegin(WorldChallenge challenge) { public void onBegin(WorldChallenge challenge) {
challenge challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 1, challenge.getScore().get()));
.getScene()
.broadcastPacket(new PacketChallengeDataNotify(challenge, 1, challenge.getScore().get()));
} }
@Override @Override
public void onMonsterDeath(WorldChallenge challenge, EntityMonster monster) { public void onMonsterDeath(WorldChallenge challenge, EntityMonster monster) {
var newScore = challenge.increaseScore(); if(monster.getConfigId() == monsterCfgId){
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 1, newScore));
if (newScore >= challenge.getGoal()) {
challenge.done(); challenge.done();
} }
} }

View File

@ -52,18 +52,25 @@ public class EntityAvatar extends GameEntity {
public EntityAvatar(Scene scene, Avatar avatar) { public EntityAvatar(Scene scene, Avatar avatar) {
super(scene); super(scene);
this.avatar = avatar; this.avatar = avatar;
this.avatar.setCurrentEnergy(); this.avatar.setCurrentEnergy();
if (getScene() != null) { if (getScene() != null) {
this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR); this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR);
GameItem weapon = getAvatar().getWeapon(); var weapon = getAvatar().getWeapon();
if (weapon != null) { if (weapon != null) {
weapon.setWeaponEntityId(getScene().getWorld().getNextEntityId(EntityIdType.WEAPON)); weapon.setWeaponEntityId(getScene().getWorld().getNextEntityId(EntityIdType.WEAPON));
} }
} }
} }
@Override
public int getEntityTypeId() {
return this.getAvatar().getAvatarId();
}
public Player getPlayer() { public Player getPlayer() {
return this.avatar.getPlayer(); return this.avatar.getPlayer();
} }
@ -153,6 +160,22 @@ public class EntityAvatar extends GameEntity {
} }
} }
/**
* Adds a fixed amount of energy to the current avatar.
*
* @param amount The amount of energy to add.
* @return True if the energy was added, false if the energy was not added.
*/
public boolean addEnergy(float amount) {
var curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
var curEnergy = this.getFightProperty(curEnergyProp);
if (curEnergy == amount) return false;
this.getAvatar().setCurrentEnergy(curEnergyProp, amount);
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp));
return true;
}
public void addEnergy(float amount, PropChangeReason reason) { public void addEnergy(float amount, PropChangeReason reason) {
this.addEnergy(amount, reason, false); this.addEnergy(amount, reason, false);
} }

View File

@ -1,6 +1,6 @@
package emu.grasscutter.game.entity; package emu.grasscutter.game.entity;
import emu.grasscutter.data.binout.ConfigGadget; import emu.grasscutter.data.binout.config.ConfigEntityGadget;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
@ -25,12 +25,17 @@ public abstract class EntityBaseGadget extends GameEntity {
public abstract int getGadgetId(); public abstract int getGadgetId();
@Override
public int getEntityTypeId() {
return this.getGadgetId();
}
@Override @Override
public void onDeath(int killerId) { public void onDeath(int killerId) {
super.onDeath(killerId); // Invoke super class's onDeath() method. super.onDeath(killerId); // Invoke super class's onDeath() method.
} }
protected void fillFightProps(ConfigGadget configGadget) { protected void fillFightProps(ConfigEntityGadget configGadget) {
if (configGadget == null || configGadget.getCombat() == null) { if (configGadget == null || configGadget.getCombat() == null) {
return; return;
} }

View File

@ -1,13 +1,15 @@
package emu.grasscutter.game.entity; package emu.grasscutter.game.entity;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.ConfigGadget; import emu.grasscutter.data.binout.config.ConfigEntityGadget;
import emu.grasscutter.data.excels.GadgetData; import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.game.entity.gadget.*; import emu.grasscutter.game.entity.gadget.*;
import emu.grasscutter.game.entity.gadget.platform.BaseRoute;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.world.SceneGroupInstance;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo; import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair; import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo; import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
@ -15,42 +17,58 @@ import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData;
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo; import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq; import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo; import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
import emu.grasscutter.net.proto.PlatformInfoOuterClass;
import emu.grasscutter.net.proto.PropPairOuterClass.PropPair; import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType; import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType;
import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo; import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo; import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo; import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.net.proto.VectorOuterClass.Vector; import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.net.proto.VisionTypeOuterClass;
import emu.grasscutter.scripts.EntityControllerScriptManager;
import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneGadget; import emu.grasscutter.scripts.data.SceneGadget;
import emu.grasscutter.scripts.data.ScriptArgs; import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketGadgetStateNotify; import emu.grasscutter.server.packet.send.PacketGadgetStateNotify;
import emu.grasscutter.server.packet.send.PacketPlatformStartRouteNotify;
import emu.grasscutter.server.packet.send.PacketPlatformStopRouteNotify;
import emu.grasscutter.server.packet.send.PacketSceneTimeNotify;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper; import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap; import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import java.util.Optional;
import javax.annotation.Nullable;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.ToString; import lombok.ToString;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
@ToString(callSuper = true) @ToString(callSuper = true)
public class EntityGadget extends EntityBaseGadget { public class EntityGadget extends EntityBaseGadget {
@Getter private final GadgetData gadgetData; @Getter private final GadgetData gadgetData;
@Getter(onMethod = @__(@Override)) @Setter
@Getter(onMethod_ = @Override, lazy = true)
private final Int2FloatMap fightProperties = new Int2FloatOpenHashMap();
@Getter(onMethod_ = @Override)
@Setter
private int gadgetId; private int gadgetId;
@Getter private final Position bornPos;
@Getter private final Position bornRot;
@Getter @Setter private GameEntity owner = null;
@Getter @Setter private List<GameEntity> children = new ArrayList<>();
@Getter @Setter private int state; @Getter private int state;
@Getter @Setter private int pointType; @Getter @Setter private int pointType;
@Getter private GadgetContent content; @Getter private GadgetContent content;
@Getter(onMethod = @__(@Override), lazy = true)
private final Int2FloatMap fightProperties = new Int2FloatOpenHashMap();
@Getter @Setter private SceneGadget metaGadget; @Getter @Setter private SceneGadget metaGadget;
@Nullable @Getter private final ConfigGadget configGadget; @Nullable @Getter
private ConfigEntityGadget configGadget;
@Getter @Setter private BaseRoute routeConfig;
@Getter @Setter private int stopValue = 0; //Controller related, inited to zero
@Getter @Setter private int startValue = 0; //Controller related, inited to zero
@Getter @Setter private int ticksSinceChange;
public EntityGadget(Scene scene, int gadgetId, Position pos) { public EntityGadget(Scene scene, int gadgetId, Position pos) {
this(scene, gadgetId, pos, null, null); this(scene, gadgetId, pos, null, null);
@ -60,27 +78,43 @@ public class EntityGadget extends EntityBaseGadget {
this(scene, gadgetId, pos, rot, null); this(scene, gadgetId, pos, rot, null);
} }
public EntityGadget( public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot, GadgetContent content) {
Scene scene, int gadgetId, Position pos, Position rot, GadgetContent content) {
super(scene, pos, rot); super(scene, pos, rot);
this.gadgetData = GameData.getGadgetDataMap().get(gadgetId); this.gadgetData = GameData.getGadgetDataMap().get(gadgetId);
this.configGadget = if (gadgetData != null && gadgetData.getJsonName() != null) {
Optional.ofNullable(this.gadgetData) this.configGadget = GameData.getGadgetConfigData().get(gadgetData.getJsonName());
.map(GadgetData::getJsonName) }
.map(GameData.getGadgetConfigData()::get)
.orElse(null);
this.id = this.getScene().getWorld().getNextEntityId(EntityIdType.GADGET); this.id = this.getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.gadgetId = gadgetId; this.gadgetId = gadgetId;
this.content = content; this.content = content;
fillFightProps(configGadget); this.bornPos = this.getPosition().clone();
this.bornRot = this.getRotation().clone();
this.fillFightProps(configGadget);
if(GameData.getGadgetMappingMap().containsKey(gadgetId)) {
String controllerName = GameData.getGadgetMappingMap().get(gadgetId).getServerController();
this.setEntityController(EntityControllerScriptManager.getGadgetController(controllerName));
}
}
public void setState(int state) {
this.state = state;
//Cache the gadget state
if(metaGadget != null && metaGadget.group != null) {
var instance = getScene().getScriptManager().getCachedGroupInstanceById(metaGadget.group.id);
if(instance != null) instance.cacheGadgetState(metaGadget, state);
}
} }
public void updateState(int state) { public void updateState(int state) {
if(state == this.getState()) return; //Don't triggers events
this.setState(state); this.setState(state);
ticksSinceChange = getScene().getSceneTimeSeconds();
this.getScene().broadcastPacket(new PacketGadgetStateNotify(this, state)); this.getScene().broadcastPacket(new PacketGadgetStateNotify(this, state));
getScene() getScene().getScriptManager().callEvent(new ScriptArgs(this.getGroupId(), EventType.EVENT_GADGET_STATE_CHANGE, state, this.getConfigId()));
.getScriptManager()
.callEvent(EventType.EVENT_GADGET_STATE_CHANGE, new ScriptArgs(state, this.getConfigId()));
} }
@Deprecated(forRemoval = true) // Dont use! @Deprecated(forRemoval = true) // Dont use!
@ -90,17 +124,14 @@ public class EntityGadget extends EntityBaseGadget {
// TODO refactor // TODO refactor
public void buildContent() { public void buildContent() {
if (this.getContent() != null if (this.getContent() != null || this.getGadgetData() == null || this.getGadgetData().getType() == null) {
|| this.getGadgetData() == null
|| this.getGadgetData().getType() == null) {
return; return;
} }
this.content = this.content = switch (this.getGadgetData().getType()) {
switch (this.getGadgetData().getType()) {
case GatherPoint -> new GadgetGatherPoint(this); case GatherPoint -> new GadgetGatherPoint(this);
case GatherObject -> new GadgetGatherObject(this); case GatherObject -> new GadgetGatherObject(this);
case Worktop -> new GadgetWorktop(this); case Worktop, SealGadget -> new GadgetWorktop(this);
case RewardStatue -> new GadgetRewardStatue(this); case RewardStatue -> new GadgetRewardStatue(this);
case Chest -> new GadgetChest(this); case Chest -> new GadgetChest(this);
case Gadget -> new GadgetObject(this); case Gadget -> new GadgetObject(this);
@ -124,9 +155,16 @@ public class EntityGadget extends EntityBaseGadget {
@Override @Override
public void onCreate() { public void onCreate() {
// Lua event // Lua event
getScene() getScene().getScriptManager().callEvent(new ScriptArgs(this.getGroupId(), EventType.EVENT_GADGET_CREATE, this.getConfigId()));
.getScriptManager() }
.callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(this.getConfigId()));
@Override
public void onRemoved() {
super.onRemoved();
if(!children.isEmpty()) {
getScene().removeEntities(children, VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE);
children.clear();
}
} }
@Override @Override
@ -139,51 +177,72 @@ public class EntityGadget extends EntityBaseGadget {
if (getScene().getChallenge() != null) { if (getScene().getChallenge() != null) {
getScene().getChallenge().onGadgetDeath(this); getScene().getChallenge().onGadgetDeath(this);
} }
getScene() getScene().getScriptManager().callEvent(new ScriptArgs(this.getGroupId(), EventType.EVENT_ANY_GADGET_DIE, this.getConfigId()));
.getScriptManager()
.callEvent(EventType.EVENT_ANY_GADGET_DIE, new ScriptArgs(this.getConfigId())); SceneGroupInstance groupInstance = getScene().getScriptManager().getCachedGroupInstanceById(this.getGroupId());
if(groupInstance != null && metaGadget != null)
groupInstance.getDeadEntities().add(metaGadget.config_id);
}
public boolean startPlatform(){
if(routeConfig == null){
return false;
}
if(routeConfig.isStarted()){
return true;
}
getScene().broadcastPacket(new PacketSceneTimeNotify(getScene()));
routeConfig.startRoute(getScene());
getScene().broadcastPacket(new PacketPlatformStartRouteNotify(this));
return true;
}
public boolean stopPlatform(){
if(routeConfig == null){
return false;
}
if(!routeConfig.isStarted()){
return true;
}
routeConfig.stopRoute(getScene());
getScene().broadcastPacket(new PacketPlatformStopRouteNotify(this));
return true;
} }
@Override @Override
public SceneEntityInfo toProto() { public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder()) .setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder()) .setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo( .setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(bornPos.toProto()))
SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder())) .setBornPos(bornPos.toProto())
.setBornPos(Vector.newBuilder())
.build(); .build();
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
SceneEntityInfo.newBuilder()
.setEntityId(getId()) .setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET) .setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setMotionInfo( .setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
MotionInfo.newBuilder()
.setPos(getPosition().toProto())
.setRot(getRotation().toProto())
.setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder()) .addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder()) .setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority) .setEntityAuthorityInfo(authority)
.setLifeState(1); .setLifeState(1);
PropPair pair = PropPair pair = PropPair.newBuilder()
PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId()) .setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1)) .setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
.build(); .build();
entityInfo.addPropList(pair); entityInfo.addPropList(pair);
// We do not use the getter to null check because the getter will create a fight prop map if it // We do not use the getter to null check because the getter will create a fight prop map if it is null
// is null
if (this.fightProperties != null) { if (this.fightProperties != null) {
addAllFightPropsToEntityInfo(entityInfo); addAllFightPropsToEntityInfo(entityInfo);
} }
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
SceneGadgetInfo.newBuilder()
.setGadgetId(this.getGadgetId()) .setGadgetId(this.getGadgetId())
.setGroupId(this.getGroupId()) .setGroupId(this.getGroupId())
.setConfigId(this.getConfigId()) .setConfigId(this.getConfigId())
@ -195,12 +254,28 @@ public class EntityGadget extends EntityBaseGadget {
gadgetInfo.setDraftId(this.metaGadget.draft_id); gadgetInfo.setDraftId(this.metaGadget.draft_id);
} }
if(owner != null){
gadgetInfo.setOwnerEntityId(owner.getId());
}
if (this.getContent() != null) { if (this.getContent() != null) {
this.getContent().onBuildProto(gadgetInfo); this.getContent().onBuildProto(gadgetInfo);
} }
if(routeConfig!=null){
gadgetInfo.setPlatform(getPlatformInfo());
}
entityInfo.setGadget(gadgetInfo); entityInfo.setGadget(gadgetInfo);
return entityInfo.build(); return entityInfo.build();
} }
public PlatformInfoOuterClass.PlatformInfo.Builder getPlatformInfo(){
if(routeConfig != null){
return routeConfig.toProto();
}
return PlatformInfoOuterClass.PlatformInfo.newBuilder();
}
} }

View File

@ -5,9 +5,12 @@ import emu.grasscutter.data.common.PropGrowCurve;
import emu.grasscutter.data.excels.EnvAnimalGatherConfigData; import emu.grasscutter.data.excels.EnvAnimalGatherConfigData;
import emu.grasscutter.data.excels.monster.MonsterCurveData; import emu.grasscutter.data.excels.monster.MonsterCurveData;
import emu.grasscutter.data.excels.monster.MonsterData; import emu.grasscutter.data.excels.monster.MonsterData;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.*; import emu.grasscutter.game.props.*;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.world.SceneGroupInstance;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo; import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair; import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo; import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
@ -22,32 +25,36 @@ import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.grasscutter.net.proto.SceneMonsterInfoOuterClass.SceneMonsterInfo; import emu.grasscutter.net.proto.SceneMonsterInfoOuterClass.SceneMonsterInfo;
import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo; import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo;
import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneMonster;
import emu.grasscutter.scripts.data.ScriptArgs; import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.event.entity.EntityDamageEvent;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper; import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import java.util.Optional;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
public class EntityMonster extends GameEntity { import java.util.Optional;
@Getter private final MonsterData monsterData;
@Getter(onMethod_ = @Override) import static emu.grasscutter.scripts.constants.EventType.EVENT_SPECIFIC_MONSTER_HP_CHANGE;
public class EntityMonster extends GameEntity {
@Getter(onMethod = @__(@Override))
private final Int2FloatOpenHashMap fightProperties; private final Int2FloatOpenHashMap fightProperties;
@Getter(onMethod_ = @Override) @Getter(onMethod = @__(@Override))
private final Position position; private final Position position;
@Getter(onMethod = @__(@Override))
@Getter(onMethod_ = @Override)
private final Position rotation; private final Position rotation;
@Getter private final MonsterData monsterData;
@Getter private final Position bornPos; @Getter private final Position bornPos;
@Getter private final int level; @Getter private final int level;
private int weaponEntityId; @Getter private int weaponEntityId;
@Getter @Setter private int poseId; @Getter @Setter private int poseId;
@Getter @Setter private int aiId = -1; @Getter @Setter private int aiId = -1;
@Getter @Setter private SceneMonster metaMonster;
public EntityMonster(Scene scene, MonsterData monsterData, Position pos, int level) { public EntityMonster(Scene scene, MonsterData monsterData, Position pos, int level) {
super(scene); super(scene);
this.id = getWorld().getNextEntityId(EntityIdType.MONSTER); this.id = getWorld().getNextEntityId(EntityIdType.MONSTER);
@ -66,6 +73,11 @@ public class EntityMonster extends GameEntity {
this.recalcStats(); this.recalcStats();
} }
@Override
public int getEntityTypeId() {
return getMonsterId();
}
public int getMonsterWeaponId() { public int getMonsterWeaponId() {
return this.getMonsterData().getWeaponId(); return this.getMonsterData().getWeaponId();
} }
@ -81,8 +93,7 @@ public class EntityMonster extends GameEntity {
@Override @Override
public void onInteract(Player player, GadgetInteractReq interactReq) { public void onInteract(Player player, GadgetInteractReq interactReq) {
EnvAnimalGatherConfigData gatherData = EnvAnimalGatherConfigData gatherData = GameData.getEnvAnimalGatherConfigDataMap().get(this.getMonsterData().getId());
GameData.getEnvAnimalGatherConfigDataMap().get(this.getMonsterData().getId());
if (gatherData == null) { if (gatherData == null) {
return; return;
@ -96,18 +107,16 @@ public class EntityMonster extends GameEntity {
@Override @Override
public void onCreate() { public void onCreate() {
// Lua event // Lua event
getScene() getScene().getScriptManager().callEvent(new ScriptArgs(this.getGroupId(), EventType.EVENT_ANY_MONSTER_LIVE, this.getConfigId()));
.getScriptManager()
.callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(this.getConfigId()));
} }
@Override @Override
public void damage(float amount, int killerId) { public void damage(float amount, int killerId, ElementType attackType) {
// Get HP before damage. // Get HP before damage.
float hpBeforeDamage = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP); float hpBeforeDamage = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
// Apply damage. // Apply damage.
super.damage(amount, killerId); super.damage(amount, killerId, attackType);
// Get HP after damage. // Get HP after damage.
float hpAfterDamage = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP); float hpAfterDamage = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
@ -118,6 +127,15 @@ public class EntityMonster extends GameEntity {
} }
} }
@Override
public void runLuaCallbacks(EntityDamageEvent event) {
super.runLuaCallbacks(event);
getScene().getScriptManager().callEvent(new ScriptArgs(this.getGroupId(), EVENT_SPECIFIC_MONSTER_HP_CHANGE, getConfigId(), monsterData.getId())
.setSourceEntityId(getId())
.setParam3((int) this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP))
.setEventSource(Integer.toString(getConfigId())));
}
@Override @Override
public void onDeath(int killerId) { public void onDeath(int killerId) {
super.onDeath(killerId); // Invoke super class's onDeath() method. super.onDeath(killerId); // Invoke super class's onDeath() method.
@ -131,22 +149,29 @@ public class EntityMonster extends GameEntity {
challenge.ifPresent(c -> c.onMonsterDeath(this)); challenge.ifPresent(c -> c.onMonsterDeath(this));
if (scriptManager.isInit() && this.getGroupId() > 0) { if (scriptManager.isInit() && this.getGroupId() > 0) {
Optional.ofNullable(scriptManager.getScriptMonsterSpawnService()) Optional.ofNullable(scriptManager.getScriptMonsterSpawnService()).ifPresent(s -> s.onMonsterDead(this));
.ifPresent(s -> s.onMonsterDead(this));
// prevent spawn monster after success // prevent spawn monster after success
if (challenge.map(c -> c.inProgress()).orElse(true)) /*if (challenge.map(c -> c.inProgress()).orElse(true)) {
scriptManager.callEvent( scriptManager.callEvent(new ScriptArgs(EventType.EVENT_ANY_MONSTER_DIE, this.getConfigId()).setGroupId(this.getGroupId()));
EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId())); } else if (getScene().getChallenge() == null) {
}*/
scriptManager.callEvent(new ScriptArgs(this.getGroupId(), EventType.EVENT_ANY_MONSTER_DIE, this.getConfigId()));
} }
// Battle Pass trigger // Battle Pass trigger
scene scene.getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_MONSTER_DIE, this.getMonsterId(), 1));
.getPlayers()
.forEach( scene.getPlayers().forEach(p -> p.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_MONSTER_DIE, this.getMonsterId()));
p -> scene.getPlayers().forEach(p -> p.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_KILL_MONSTER, this.getMonsterId()));
p.getBattlePassManager() scene.getPlayers().forEach(p -> p.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_CLEAR_GROUP_MONSTER, this.getGroupId()));
.triggerMission(
WatcherTriggerType.TRIGGER_MONSTER_DIE, this.getMonsterId(), 1)); SceneGroupInstance groupInstance = scene.getScriptManager().getGroupInstanceById(this.getGroupId());
if(groupInstance != null && metaMonster != null)
groupInstance.getDeadEntities().add(metaMonster.config_id);
scene.triggerDungeonEvent(DungeonPassConditionType.DUNGEON_COND_KILL_GROUP_MONSTER, this.getGroupId());
scene.triggerDungeonEvent(DungeonPassConditionType.DUNGEON_COND_KILL_TYPE_MONSTER, this.getMonsterData().getType().getValue());
scene.triggerDungeonEvent(DungeonPassConditionType.DUNGEON_COND_KILL_MONSTER, this.getMonsterId());
} }
public void recalcStats() { public void recalcStats() {
@ -154,59 +179,41 @@ public class EntityMonster extends GameEntity {
MonsterData data = this.getMonsterData(); MonsterData data = this.getMonsterData();
// Get hp percent, set to 100% if none // Get hp percent, set to 100% if none
float hpPercent = float hpPercent = this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) <= 0 ? 1f : this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) / this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) <= 0
? 1f
: this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP)
/ this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
// Clear properties // Clear properties
this.getFightProperties().clear(); this.getFightProperties().clear();
// Base stats // Base stats
MonsterData.definedFightProperties.forEach( MonsterData.definedFightProperties.forEach(prop -> this.setFightProperty(prop, data.getFightProperty(prop)));
prop -> this.setFightProperty(prop, data.getFightProperty(prop)));
// Level curve // Level curve
MonsterCurveData curve = GameData.getMonsterCurveDataMap().get(this.getLevel()); MonsterCurveData curve = GameData.getMonsterCurveDataMap().get(this.getLevel());
if (curve != null) { if (curve != null) {
for (PropGrowCurve growCurve : data.getPropGrowCurves()) { for (PropGrowCurve growCurve : data.getPropGrowCurves()) {
FightProperty prop = FightProperty.getPropByName(growCurve.getType()); FightProperty prop = FightProperty.getPropByName(growCurve.getType());
this.setFightProperty( this.setFightProperty(prop, this.getFightProperty(prop) * curve.getMultByProp(growCurve.getGrowCurve()));
prop, this.getFightProperty(prop) * curve.getMultByProp(growCurve.getGrowCurve()));
} }
} }
// Set % stats // Set % stats
FightProperty.forEachCompoundProperty( FightProperty.forEachCompoundProperty(c -> this.setFightProperty(c.getResult(),
c -> this.getFightProperty(c.getFlat()) + (this.getFightProperty(c.getBase()) * (1f + this.getFightProperty(c.getPercent())))));
this.setFightProperty(
c.getResult(),
this.getFightProperty(c.getFlat())
+ (this.getFightProperty(c.getBase())
* (1f + this.getFightProperty(c.getPercent())))));
// Set current hp // Set current hp
this.setFightProperty( this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * hpPercent);
FightProperty.FIGHT_PROP_CUR_HP,
this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * hpPercent);
} }
@Override @Override
public SceneEntityInfo toProto() { public SceneEntityInfo toProto() {
var authority = var authority = EntityAuthorityInfo.newBuilder()
EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder()) .setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder()) .setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo( .setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(this.getBornPos().toProto()))
SceneEntityAiInfo.newBuilder()
.setIsAiOpen(true)
.setBornPos(this.getBornPos().toProto()))
.setBornPos(this.getBornPos().toProto()) .setBornPos(this.getBornPos().toProto())
.build(); .build();
var entityInfo = var entityInfo = SceneEntityInfo.newBuilder()
SceneEntityInfo.newBuilder()
.setEntityId(getId()) .setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_MONSTER) .setEntityType(ProtEntityType.PROT_ENTITY_TYPE_MONSTER)
.setMotionInfo(this.getMotionInfo()) .setMotionInfo(this.getMotionInfo())
@ -217,31 +224,29 @@ public class EntityMonster extends GameEntity {
this.addAllFightPropsToEntityInfo(entityInfo); this.addAllFightPropsToEntityInfo(entityInfo);
entityInfo.addPropList( entityInfo.addPropList(PropPair.newBuilder()
PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId()) .setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getLevel())) .setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getLevel()))
.build()); .build());
var monsterInfo = var monsterInfo = SceneMonsterInfo.newBuilder()
SceneMonsterInfo.newBuilder()
.setMonsterId(getMonsterId()) .setMonsterId(getMonsterId())
.setGroupId(this.getGroupId()) .setGroupId(this.getGroupId())
.setConfigId(this.getConfigId()) .setConfigId(this.getConfigId())
.addAllAffixList(getMonsterData().getAffix()) .addAllAffixList(getMonsterData().getAffix())
.setAuthorityPeerId(getWorld().getHostPeerId()) .setAuthorityPeerId(getWorld().getHostPeerId())
.setPoseId(this.getPoseId()) .setPoseId(this.getPoseId())
.setBlockId(3001) .setBlockId(getScene().getId())
.setBornType(MonsterBornType.MONSTER_BORN_TYPE_DEFAULT) .setBornType(MonsterBornType.MONSTER_BORN_TYPE_DEFAULT);
.setSpecialNameId(40);
if (getMonsterData().getDescribeData() != null) { if (getMonsterData().getDescribeData() != null) {
monsterInfo.setTitleId(getMonsterData().getDescribeData().getTitleID()); monsterInfo.setTitleId(getMonsterData().getDescribeData().getTitleId())
.setSpecialNameId(getMonsterData().getSpecialNameId());
} }
if (this.getMonsterWeaponId() > 0) { if (this.getMonsterWeaponId() > 0) {
SceneWeaponInfo weaponInfo = SceneWeaponInfo weaponInfo = SceneWeaponInfo.newBuilder()
SceneWeaponInfo.newBuilder()
.setEntityId(this.weaponEntityId) .setEntityId(this.weaponEntityId)
.setGadgetId(this.getMonsterWeaponId()) .setGadgetId(this.getMonsterWeaponId())
.setAbilityInfo(AbilitySyncStateInfo.newBuilder()) .setAbilityInfo(AbilitySyncStateInfo.newBuilder())

View File

@ -30,6 +30,11 @@ public class EntityNPC extends GameEntity {
this.metaNpc = metaNPC; this.metaNpc = metaNPC;
} }
@Override
public int getEntityTypeId() {
return this.metaNpc.npc_id;
}
@Override @Override
public Int2FloatMap getFightProperties() { public Int2FloatMap getFightProperties() {
return null; return null;

View File

@ -20,15 +20,21 @@ public class EntityRegion extends GameEntity {
public EntityRegion(Scene scene, SceneRegion region) { public EntityRegion(Scene scene, SceneRegion region) {
super(scene); super(scene);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.REGION); this.id = getScene().getWorld().getNextEntityId(EntityIdType.REGION);
setGroupId(region.group.id); this.setGroupId(region.group.id);
setBlockId(region.group.block_id); this.setBlockId(region.group.block_id);
setConfigId(region.config_id); this.setConfigId(region.config_id);
this.position = region.pos.clone(); this.position = region.pos.clone();
this.entities = ConcurrentHashMap.newKeySet(); this.entities = ConcurrentHashMap.newKeySet();
this.metaRegion = region; this.metaRegion = region;
} }
@Override
public int getEntityTypeId() {
return this.metaRegion.config_id;
}
public void addEntity(GameEntity entity) { public void addEntity(GameEntity entity) {
if (this.getEntities().contains(entity.getId())) { if (this.getEntities().contains(entity.getId())) {
return; return;

View File

@ -1,6 +1,5 @@
package emu.grasscutter.game.entity; package emu.grasscutter.game.entity;
import emu.grasscutter.game.entity.platform.EntityPlatform;
import emu.grasscutter.game.entity.platform.EntitySolarIsotomaElevatorPlatform; import emu.grasscutter.game.entity.platform.EntitySolarIsotomaElevatorPlatform;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
@ -10,19 +9,16 @@ import lombok.Getter;
public class EntitySolarIsotomaClientGadget extends EntityClientGadget { public class EntitySolarIsotomaClientGadget extends EntityClientGadget {
public static final int GADGET_ID = 41038001; public static final int GADGET_ID = 41038001;
public static final int ELEVATOR_GADGET_ID = 41038002; public static final int ELEVATOR_GADGET_ID = 41038002;
@Getter private EntityPlatform platformGadget; @Getter private EntityGadget platformGadget;
public EntitySolarIsotomaClientGadget( public EntitySolarIsotomaClientGadget(Scene scene, Player player, EvtCreateGadgetNotifyOuterClass.EvtCreateGadgetNotify notify) {
Scene scene, Player player, EvtCreateGadgetNotifyOuterClass.EvtCreateGadgetNotify notify) {
super(scene, player, notify); super(scene, player, notify);
} }
@Override @Override
public void onCreate() { public void onCreate() {
//Create solar isotoma elevator and send to all. //Create solar isotoma elevator and send to all.
this.platformGadget = this.platformGadget = new EntitySolarIsotomaElevatorPlatform(this, getScene(), ELEVATOR_GADGET_ID, getPosition(), getRotation());
new EntitySolarIsotomaElevatorPlatform(
this, getScene(), getOwner(), ELEVATOR_GADGET_ID, getPosition(), getRotation());
getScene().addEntity(this.platformGadget); getScene().addEntity(this.platformGadget);
} }

View File

@ -1,7 +1,7 @@
package emu.grasscutter.game.entity; package emu.grasscutter.game.entity;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.ConfigGadget; import emu.grasscutter.data.binout.config.ConfigEntityGadget;
import emu.grasscutter.data.excels.GadgetData; import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.EntityIdType;
@ -25,17 +25,17 @@ import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper; import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap; import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
public class EntityVehicle extends EntityBaseGadget { public class EntityVehicle extends EntityBaseGadget {
@Getter private final Player owner; @Getter private final Player owner;
@Getter(onMethod = @__(@Override))
@Getter(onMethod_ = @Override)
private final Int2FloatMap fightProperties; private final Int2FloatMap fightProperties;
@Getter private final int pointId; @Getter private final int pointId;
@ -43,10 +43,9 @@ public class EntityVehicle extends EntityBaseGadget {
@Getter @Setter private float curStamina; @Getter @Setter private float curStamina;
@Getter private final List<VehicleMember> vehicleMembers; @Getter private final List<VehicleMember> vehicleMembers;
@Nullable @Getter private ConfigGadget configGadget; @Nullable @Getter private ConfigEntityGadget configGadget;
public EntityVehicle( public EntityVehicle(Scene scene, Player player, int gadgetId, int pointId, Position pos, Position rot) {
Scene scene, Player player, int gadgetId, int pointId, Position pos, Position rot) {
super(scene, pos, rot); super(scene, pos, rot);
this.owner = player; this.owner = player;
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET); this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
@ -64,7 +63,7 @@ public class EntityVehicle extends EntityBaseGadget {
} }
@Override @Override
protected void fillFightProps(ConfigGadget configGadget) { protected void fillFightProps(ConfigEntityGadget configGadget) {
super.fillFightProps(configGadget); super.fillFightProps(configGadget);
this.addFightProperty(FightProperty.FIGHT_PROP_CUR_SPEED, 0); this.addFightProperty(FightProperty.FIGHT_PROP_CUR_SPEED, 0);
this.addFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, 0); this.addFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, 0);
@ -73,46 +72,34 @@ public class EntityVehicle extends EntityBaseGadget {
@Override @Override
public SceneEntityInfo toProto() { public SceneEntityInfo toProto() {
VehicleInfo vehicle = VehicleInfo vehicle = VehicleInfo.newBuilder()
VehicleInfo.newBuilder()
.setOwnerUid(this.owner.getUid()) .setOwnerUid(this.owner.getUid())
.setCurStamina(getCurStamina()) .setCurStamina(getCurStamina())
.build(); .build();
EntityAuthorityInfo authority = EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder()) .setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder()) .setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo( .setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(getPosition().toProto()))
SceneEntityAiInfo.newBuilder()
.setIsAiOpen(true)
.setBornPos(getPosition().toProto()))
.setBornPos(getPosition().toProto()) .setBornPos(getPosition().toProto())
.build(); .build();
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
SceneGadgetInfo.newBuilder()
.setGadgetId(this.getGadgetId()) .setGadgetId(this.getGadgetId())
.setAuthorityPeerId(this.getOwner().getPeerId()) .setAuthorityPeerId(this.getOwner().getPeerId())
.setIsEnableInteract(true) .setIsEnableInteract(true)
.setVehicleInfo(vehicle); .setVehicleInfo(vehicle);
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
SceneEntityInfo.newBuilder()
.setEntityId(getId()) .setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET) .setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setMotionInfo( .setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
MotionInfo.newBuilder()
.setPos(getPosition().toProto())
.setRot(getRotation().toProto())
.setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder()) .addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setGadget(gadgetInfo) .setGadget(gadgetInfo)
.setEntityAuthorityInfo(authority) .setEntityAuthorityInfo(authority)
.setLifeState(1); .setLifeState(1);
PropPair pair = PropPair pair = PropPair.newBuilder()
PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId()) .setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 47)) .setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 47))
.build(); .build();

View File

@ -1,6 +1,7 @@
package emu.grasscutter.game.entity; package emu.grasscutter.game.entity;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.LifeState; import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
@ -12,6 +13,7 @@ import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState; import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo; import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.grasscutter.net.proto.VectorOuterClass.Vector; import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.scripts.data.controller.EntityController;
import emu.grasscutter.server.event.entity.EntityDamageEvent; import emu.grasscutter.server.event.entity.EntityDamageEvent;
import emu.grasscutter.server.event.entity.EntityDeathEvent; import emu.grasscutter.server.event.entity.EntityDeathEvent;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
@ -39,6 +41,10 @@ public abstract class GameEntity {
@Getter @Setter private boolean lockHP; @Getter @Setter private boolean lockHP;
// Lua controller for specific actions
@Getter @Setter private EntityController entityController;
@Getter private ElementType lastAttackType = ElementType.None;
// Abilities // Abilities
private Object2FloatMap<String> metaOverrideMap; private Object2FloatMap<String> metaOverrideMap;
private Int2ObjectMap<String> metaModifiers; private Int2ObjectMap<String> metaModifiers;
@ -52,6 +58,8 @@ public abstract class GameEntity {
return this.getId() >> 24; return this.getId() >> 24;
} }
public abstract int getEntityTypeId();
public World getWorld() { public World getWorld() {
return this.getScene().getWorld(); return this.getScene().getWorld();
} }
@ -115,15 +123,12 @@ public abstract class GameEntity {
} }
protected MotionInfo getMotionInfo() { protected MotionInfo getMotionInfo() {
MotionInfo proto = return MotionInfo.newBuilder()
MotionInfo.newBuilder()
.setPos(this.getPosition().toProto()) .setPos(this.getPosition().toProto())
.setRot(this.getRotation().toProto()) .setRot(this.getRotation().toProto())
.setSpeed(Vector.newBuilder()) .setSpeed(Vector.newBuilder())
.setState(this.getMotionState()) .setState(this.getMotionState())
.build(); .build();
return proto;
} }
public float heal(float amount) { public float heal(float amount) {
@ -149,10 +154,10 @@ public abstract class GameEntity {
} }
public void damage(float amount) { public void damage(float amount) {
this.damage(amount, 0); this.damage(amount, 0, ElementType.None);
} }
public void damage(float amount, int killerId) { public void damage(float amount, int killerId, ElementType attackType) {
// Check if the entity has properties. // Check if the entity has properties.
if (this.getFightProperties() == null || !hasFightProperty(FightProperty.FIGHT_PROP_CUR_HP)) { if (this.getFightProperties() == null || !hasFightProperty(FightProperty.FIGHT_PROP_CUR_HP)) {
return; return;
@ -160,7 +165,7 @@ public abstract class GameEntity {
// Invoke entity damage event. // Invoke entity damage event.
EntityDamageEvent event = EntityDamageEvent event =
new EntityDamageEvent(this, amount, this.getScene().getEntityById(killerId)); new EntityDamageEvent(this, amount, attackType, this.getScene().getEntityById(killerId));
event.call(); event.call();
if (event.isCanceled()) { if (event.isCanceled()) {
return; // If the event is canceled, do not damage the entity. return; // If the event is canceled, do not damage the entity.
@ -190,6 +195,17 @@ public abstract class GameEntity {
} }
} }
/**
* Runs the Lua callbacks for {@link EntityDamageEvent}.
*
* @param event The damage event.
*/
public void runLuaCallbacks(EntityDamageEvent event) {
if (entityController != null) {
entityController.onBeHurt(this, event.getAttackElementType(), true);//todo is host handling
}
}
/** /**
* Move this entity to a new position. * Move this entity to a new position.
* *
@ -215,6 +231,19 @@ public abstract class GameEntity {
public void onRemoved() {} public void onRemoved() {}
public void onTick(int sceneTime) {
if (entityController != null) {
entityController.onTimer(this, sceneTime);
}
}
public int onClientExecuteRequest(int param1, int param2, int param3) {
if (entityController != null) {
return entityController.onClientExecuteRequest(this, param1, param2, param3);
}
return 0;
}
/** /**
* Called when this entity dies * Called when this entity dies
* *
@ -224,6 +253,11 @@ public abstract class GameEntity {
// Invoke entity death event. // Invoke entity death event.
EntityDeathEvent event = new EntityDeathEvent(this, killerId); EntityDeathEvent event = new EntityDeathEvent(this, killerId);
event.call(); event.call();
// Run Lua callbacks.
if (entityController != null) {
entityController.onDie(this, getLastAttackType());
}
} }
public abstract SceneEntityInfo toProto(); public abstract SceneEntityInfo toProto();

View File

@ -5,19 +5,22 @@ import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq; import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType; import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
import emu.grasscutter.net.proto.ResinCostTypeOuterClass;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo; import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp; import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
public class GadgetRewardStatue extends GadgetContent { public final class GadgetRewardStatue extends GadgetContent {
public GadgetRewardStatue(EntityGadget gadget) { public GadgetRewardStatue(EntityGadget gadget) {
super(gadget); super(gadget);
} }
public boolean onInteract(Player player, GadgetInteractReq req) { public boolean onInteract(Player player, GadgetInteractReq req) {
if (player.getScene().getChallenge() != null var dungeonManager = player.getScene().getDungeonManager();
&& player.getScene().getChallenge() instanceof DungeonChallenge dungeonChallenge) {
dungeonChallenge.getStatueDrops(player, req); if (player.getScene().getChallenge() instanceof DungeonChallenge) {
var useCondensed = req.getResinCostType() == ResinCostTypeOuterClass.ResinCostType.RESIN_COST_TYPE_CONDENSE;
dungeonManager.getStatueDrops(player, useCondensed, getGadget().getGroupId());
} }
player.sendPacket( player.sendPacket(

View File

@ -1,101 +0,0 @@
package emu.grasscutter.game.entity.platform;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.ConfigGadget;
import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.game.entity.EntityBaseGadget;
import emu.grasscutter.game.entity.EntityClientGadget;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.*;
import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import javax.annotation.Nullable;
import lombok.Getter;
import lombok.Setter;
public class EntityPlatform extends EntityBaseGadget {
@Getter private final Player owner;
@Getter(onMethod_ = @Override)
private final int gadgetId;
@Getter private final EntityClientGadget gadget;
@Getter(onMethod_ = @Override)
private final Int2FloatMap fightProperties;
@Getter private final MovingPlatformTypeOuterClass.MovingPlatformType movingPlatformType;
@Nullable @Getter private ConfigGadget configGadget;
@Getter @Setter private boolean isStarted;
@Getter @Setter private boolean isActive;
public EntityPlatform(
EntityClientGadget gadget,
Scene scene,
Player player,
int gadgetId,
Position pos,
Position rot,
MovingPlatformTypeOuterClass.MovingPlatformType movingPlatformType) {
super(scene, pos, rot);
this.gadget = gadget;
this.owner = player;
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.fightProperties = new Int2FloatOpenHashMap();
this.movingPlatformType = movingPlatformType;
this.gadgetId = gadgetId;
GadgetData data = GameData.getGadgetDataMap().get(gadgetId);
if (data != null && data.getJsonName() != null) {
this.configGadget = GameData.getGadgetConfigData().get(data.getJsonName());
}
fillFightProps(configGadget);
}
@Override
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
var platform =
PlatformInfoOuterClass.PlatformInfo.newBuilder()
.setMovingPlatformType(movingPlatformType)
.build();
var gadgetInfo =
SceneGadgetInfoOuterClass.SceneGadgetInfo.newBuilder()
.setGadgetId(getGadgetId())
.setAuthorityPeerId(getOwner().getPeerId())
.setPlatform(platform);
var entityInfo =
SceneEntityInfoOuterClass.SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityTypeOuterClass.ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setGadget(gadgetInfo)
.setLifeState(1);
this.addAllFightPropsToEntityInfo(entityInfo);
return entityInfo.build();
}
public PlatformInfoOuterClass.PlatformInfo onStartRoute() {
return PlatformInfoOuterClass.PlatformInfo.newBuilder()
.setStartSceneTime(getScene().getSceneTime())
.setIsStarted(true)
.setPosOffset(getPosition().toProto())
.setMovingPlatformType(getMovingPlatformType())
.setIsActive(true)
.build();
}
public PlatformInfoOuterClass.PlatformInfo onStopRoute() {
var sceneTime = getScene().getSceneTime();
return PlatformInfoOuterClass.PlatformInfo.newBuilder()
.setStartSceneTime(sceneTime)
.setStopSceneTime(sceneTime)
.setPosOffset(getPosition().toProto())
.setMovingPlatformType(getMovingPlatformType())
.build();
}
}

View File

@ -1,38 +1,23 @@
package emu.grasscutter.game.entity.platform; package emu.grasscutter.game.entity.platform;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.binout.ConfigGadget; import emu.grasscutter.data.binout.config.ConfigEntityGadget;
import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.entity.EntitySolarIsotomaClientGadget; import emu.grasscutter.game.entity.gadget.GadgetAbility;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.gadget.platform.AbilityRoute;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.*;
import emu.grasscutter.server.packet.send.PacketSceneTimeNotify;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
public class EntitySolarIsotomaElevatorPlatform extends EntityPlatform { public class EntitySolarIsotomaElevatorPlatform extends EntityGadget {
public EntitySolarIsotomaElevatorPlatform( public EntitySolarIsotomaElevatorPlatform(EntitySolarIsotomaClientGadget isotoma, Scene scene, int gadgetId, Position pos, Position rot) {
EntitySolarIsotomaClientGadget isotoma, super(scene, gadgetId, pos, rot);
Scene scene, setOwner(isotoma);
Player player, this.setRouteConfig(new AbilityRoute(rot, false, false, pos));
int gadgetId, this.setContent(new GadgetAbility(this, isotoma));
Position pos,
Position rot) {
super(
isotoma,
scene,
player,
gadgetId,
pos,
rot,
MovingPlatformTypeOuterClass.MovingPlatformType.MOVING_PLATFORM_TYPE_ABILITY);
} }
@Override @Override
protected void fillFightProps(ConfigGadget configGadget) { protected void fillFightProps(ConfigEntityGadget configGadget) {
if (configGadget == null || configGadget.getCombat() == null) { if (configGadget == null || configGadget.getCombat() == null) {
return; return;
} }
@ -41,9 +26,9 @@ public class EntitySolarIsotomaElevatorPlatform extends EntityPlatform {
if (combatProperties.isUseCreatorProperty()) { if (combatProperties.isUseCreatorProperty()) {
//If useCreatorProperty == true, use owner's property; //If useCreatorProperty == true, use owner's property;
GameEntity ownerAvatar = getScene().getEntityById(getGadget().getOwnerEntityId()); GameEntity ownerEntity = getOwner();
if (ownerAvatar != null) { if (ownerEntity != null) {
getFightProperties().putAll(ownerAvatar.getFightProperties()); getFightProperties().putAll(ownerEntity.getFightProperties());
return; return;
} else { } else {
Grasscutter.getLogger().warn("Why gadget owner is null?"); Grasscutter.getLogger().warn("Why gadget owner is null?");
@ -52,102 +37,4 @@ public class EntitySolarIsotomaElevatorPlatform extends EntityPlatform {
super.fillFightProps(configGadget); super.fillFightProps(configGadget);
} }
@Override
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
var gadget =
SceneGadgetInfoOuterClass.SceneGadgetInfo.newBuilder()
.setGadgetId(getGadgetId())
.setOwnerEntityId(getGadget().getId())
.setAuthorityPeerId(getOwner().getPeerId())
.setIsEnableInteract(true)
.setAbilityGadget(
AbilityGadgetInfoOuterClass.AbilityGadgetInfo.newBuilder()
.setCampId(getGadget().getCampId())
.setCampTargetType(getGadget().getCampType())
.setTargetEntityId(getGadget().getId())
.build())
.setPlatform(
PlatformInfoOuterClass.PlatformInfo.newBuilder()
.setStartRot(
MathQuaternionOuterClass.MathQuaternion.newBuilder().setW(1.0F).build())
.setPosOffset(getGadget().getPosition().toProto())
.setRotOffset(
MathQuaternionOuterClass.MathQuaternion.newBuilder().setW(1.0F).build())
.setMovingPlatformType(
MovingPlatformTypeOuterClass.MovingPlatformType
.MOVING_PLATFORM_TYPE_ABILITY)
.build())
.build();
var authority =
EntityAuthorityInfoOuterClass.EntityAuthorityInfo.newBuilder()
.setAiInfo(
SceneEntityAiInfoOuterClass.SceneEntityAiInfo.newBuilder()
.setIsAiOpen(true)
.setBornPos(getGadget().getPosition().toProto()))
.setBornPos(getGadget().getPosition().toProto())
.build();
var info =
SceneEntityInfoOuterClass.SceneEntityInfo.newBuilder()
.setEntityType(ProtEntityTypeOuterClass.ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setEntityId(getId())
.setMotionInfo(
MotionInfoOuterClass.MotionInfo.newBuilder()
.setPos(getGadget().getPosition().toProto())
.setRot(getGadget().getRotation().toProto())
.build());
GameEntity entity = getScene().getEntityById(getGadget().getOwnerEntityId());
if (entity instanceof EntityAvatar avatar) {
info.addPropList(
PropPairOuterClass.PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(
ProtoHelper.newPropValue(
PlayerProperty.PROP_LEVEL, avatar.getAvatar().getLevel()))
.build());
} else {
Grasscutter.getLogger().warn("Why gadget owner doesn't exist?");
}
this.addAllFightPropsToEntityInfo(info);
info.setLifeState(1).setGadget(gadget).setEntityAuthorityInfo(authority);
return info.build();
}
@Override
public PlatformInfoOuterClass.PlatformInfo onStartRoute() {
setStarted(true);
setActive(true);
var sceneTime = getScene().getSceneTime();
getOwner().sendPacket(new PacketSceneTimeNotify(getOwner()));
return PlatformInfoOuterClass.PlatformInfo.newBuilder()
.setStartSceneTime(sceneTime + 300)
.setIsStarted(true)
.setPosOffset(getPosition().toProto())
.setRotOffset(MathQuaternionOuterClass.MathQuaternion.newBuilder().setW(1.0F).build())
.setMovingPlatformType(getMovingPlatformType())
.setIsActive(true)
.build();
}
@Override
public PlatformInfoOuterClass.PlatformInfo onStopRoute() {
setStarted(false);
setActive(false);
return PlatformInfoOuterClass.PlatformInfo.newBuilder()
.setStartSceneTime(getScene().getSceneTime())
.setStopSceneTime(getScene().getSceneTime())
.setPosOffset(getPosition().toProto())
.setRotOffset(MathQuaternionOuterClass.MathQuaternion.newBuilder().setW(1.0F).build())
.setMovingPlatformType(getMovingPlatformType())
.build();
}
} }

View File

@ -1,11 +1,9 @@
package emu.grasscutter.game.managers.blossom; package emu.grasscutter.game.managers.blossom;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.monster.MonsterData;
import emu.grasscutter.data.excels.world.WorldLevelData;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge; import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger; import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger; import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterCountTrigger;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
@ -15,28 +13,27 @@ import emu.grasscutter.scripts.data.SceneGadget;
import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
public class BlossomActivity { public final class BlossomActivity {
private static final int BLOOMING_GADGET_ID = 70210109;
private final SceneGroup tempSceneGroup; private final SceneGroup tempSceneGroup;
private final WorldChallenge challenge; private final WorldChallenge challenge;
private final EntityGadget gadget; private final EntityGadget gadget;
private final int goal;
private final int worldLevel;
private final List<EntityMonster> activeMonsters = new ArrayList<>();
private final Queue<Integer> candidateMonsters = new ArrayDeque<>();
private EntityGadget chest; private EntityGadget chest;
private int step; private int step;
private final int goal;
private int generatedCount; private int generatedCount;
private final int worldLevel;
private boolean pass=false; private boolean pass=false;
private final List<EntityMonster> activeMonsters = new ArrayList<>();
public BlossomActivity( private final Queue<Integer> candidateMonsters = new ArrayDeque<>();
EntityGadget entityGadget, List<Integer> monsters, int timeout, int worldLevel) { private static final int BLOOMING_GADGET_ID = 70210109;
public BlossomActivity(EntityGadget entityGadget, List<Integer> monsters, int timeout, int worldLevel) {
this.tempSceneGroup = new SceneGroup(); this.tempSceneGroup = new SceneGroup();
this.tempSceneGroup.id = entityGadget.getId(); this.tempSceneGroup.id = entityGadget.getId();
this.gadget=entityGadget; this.gadget=entityGadget;
@ -45,24 +42,19 @@ public class BlossomActivity {
this.candidateMonsters.addAll(monsters); this.candidateMonsters.addAll(monsters);
this.worldLevel = worldLevel; this.worldLevel = worldLevel;
ArrayList<ChallengeTrigger> challengeTriggers = new ArrayList<>(); ArrayList<ChallengeTrigger> challengeTriggers = new ArrayList<>();
this.challenge = this.challenge = new WorldChallenge(entityGadget.getScene(),
new WorldChallenge(
entityGadget.getScene(),
tempSceneGroup, tempSceneGroup,
1, 1,
1, 1,
List.of(goal, timeout), List.of(goal, timeout),
timeout, timeout,
goal, goal, challengeTriggers);
challengeTriggers); challengeTriggers.add(new KillMonsterCountTrigger());
challengeTriggers.add(new KillMonsterTrigger());
//this.challengeTriggers.add(new InTimeTrigger()); //this.challengeTriggers.add(new InTimeTrigger());
} }
public WorldChallenge getChallenge() { public WorldChallenge getChallenge() {
return this.challenge; return this.challenge;
} }
public void setMonsters(List<EntityMonster> monsters) { public void setMonsters(List<EntityMonster> monsters) {
this.activeMonsters.clear(); this.activeMonsters.clear();
this.activeMonsters.addAll(monsters); this.activeMonsters.addAll(monsters);
@ -70,7 +62,6 @@ public class BlossomActivity {
monster.setGroupId(this.tempSceneGroup.id); monster.setGroupId(this.tempSceneGroup.id);
} }
} }
public int getAliveMonstersCount() { public int getAliveMonstersCount() {
int count=0; int count=0;
for (EntityMonster monster: activeMonsters) { for (EntityMonster monster: activeMonsters) {
@ -80,15 +71,12 @@ public class BlossomActivity {
} }
return count; return count;
} }
public boolean getPass() { public boolean getPass() {
return pass; return pass;
} }
public void start() { public void start() {
challenge.start(); challenge.start();
} }
public void onTick() { public void onTick() {
Scene scene = gadget.getScene(); Scene scene = gadget.getScene();
Position pos = gadget.getPosition(); Position pos = gadget.getPosition();
@ -96,7 +84,7 @@ public class BlossomActivity {
if (generatedCount<goal) { if (generatedCount<goal) {
step++; step++;
WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(worldLevel); var worldLevelData = GameData.getWorldLevelDataMap().get(worldLevel);
int worldLevelOverride = 0; int worldLevelOverride = 0;
if (worldLevelData != null) { if (worldLevelData != null) {
worldLevelOverride = worldLevelData.getMonsterLevel(); worldLevelOverride = worldLevelData.getMonsterLevel();
@ -109,7 +97,7 @@ public class BlossomActivity {
} }
generatedCount+=willSpawn; generatedCount+=willSpawn;
for (int i = 0; i < willSpawn; i++) { for (int i = 0; i < willSpawn; i++) {
MonsterData monsterData = GameData.getMonsterDataMap().get(candidateMonsters.poll()); var monsterData = GameData.getMonsterDataMap().get(candidateMonsters.poll());
int level = scene.getEntityLevel(1, worldLevelOverride); int level = scene.getEntityLevel(1, worldLevelOverride);
EntityMonster entity = new EntityMonster(scene, monsterData, pos.nearby2d(4f), level); EntityMonster entity = new EntityMonster(scene, monsterData, pos.nearby2d(4f), level);
scene.addEntity(entity); scene.addEntity(entity);
@ -124,15 +112,12 @@ public class BlossomActivity {
} }
} }
} }
public EntityGadget getGadget() { public EntityGadget getGadget() {
return gadget; return gadget;
} }
public EntityGadget getChest() { public EntityGadget getChest() {
if (chest==null) { if (chest==null) {
EntityGadget rewardGadget = EntityGadget rewardGadget = new EntityGadget(gadget.getScene(), BLOOMING_GADGET_ID, gadget.getPosition());
new EntityGadget(gadget.getScene(), BLOOMING_GADGET_ID, gadget.getPosition());
SceneGadget metaGadget = new SceneGadget(); SceneGadget metaGadget = new SceneGadget();
metaGadget.boss_chest = new SceneBossChest(); metaGadget.boss_chest = new SceneBossChest();
metaGadget.boss_chest.resin = 20; metaGadget.boss_chest.resin = 20;

View File

@ -30,6 +30,8 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import lombok.Getter;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
@ -40,7 +42,7 @@ public class EnergyManager extends BasePlayerManager {
private static final Int2ObjectMap<List<SkillParticleGenerationInfo>> private static final Int2ObjectMap<List<SkillParticleGenerationInfo>>
skillParticleGenerationData = new Int2ObjectOpenHashMap<>(); skillParticleGenerationData = new Int2ObjectOpenHashMap<>();
private final Object2IntMap<EntityAvatar> avatarNormalProbabilities; private final Object2IntMap<EntityAvatar> avatarNormalProbabilities;
private boolean energyUsage; // Should energy usage be enabled for this player? @Getter private boolean energyUsage; // Should energy usage be enabled for this player?
public EnergyManager(Player player) { public EnergyManager(Player player) {
super(player); super(player);
@ -381,8 +383,28 @@ public class EnergyManager extends BasePlayerManager {
.findFirst(); .findFirst();
} }
public boolean getEnergyUsage() { /**
return this.energyUsage; * Refills the energy of the active avatar.
*
* @return True if the energy was refilled, false otherwise.
*/
public boolean refillActiveEnergy() {
var activeEntity = this.player.getTeamManager().getCurrentAvatarEntity();
return activeEntity.addEnergy(activeEntity.getAvatar().getSkillDepot().getEnergySkillData().getCostElemVal());
}
/**
* Refills the energy of the entire team.
*
* @param changeReason The reason for the energy change.
* @param isFlat Whether the energy should be added as a flat value.
*/
public void refillTeamEnergy(PropChangeReason changeReason, boolean isFlat) {
for (var entityAvatar : this.player.getTeamManager().getActiveTeam()) {
// giving the exact amount read off the AvatarSkillData.json
entityAvatar.addEnergy(entityAvatar.getAvatar().getSkillDepot()
.getEnergySkillData().getCostElemVal(), changeReason, isFlat);
}
} }
public void setEnergyUsage(boolean energyUsage) { public void setEnergyUsage(boolean energyUsage) {

View File

@ -294,7 +294,7 @@ public class StaminaManager extends BasePlayerManager {
// Returns new stamina and sends PlayerPropNotify or VehicleStaminaNotify // Returns new stamina and sends PlayerPropNotify or VehicleStaminaNotify
public int setStamina(GameSession session, String reason, int newStamina, boolean isCharacterStamina) { public int setStamina(GameSession session, String reason, int newStamina, boolean isCharacterStamina) {
// Target Player // Target Player
if (!GAME_OPTIONS.staminaUsage || session.getPlayer().getUnlimitedStamina()) { if (!GAME_OPTIONS.staminaUsage || session.getPlayer().isUnlimitedStamina()) {
newStamina = getMaxCharacterStamina(); newStamina = getMaxCharacterStamina();
} }

View File

@ -45,7 +45,6 @@ import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.props.WatcherTriggerType; import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.quest.QuestManager; import emu.grasscutter.game.quest.QuestManager;
import emu.grasscutter.game.quest.enums.QuestContent; import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.game.quest.enums.QuestTrigger;
import emu.grasscutter.game.shop.ShopLimit; import emu.grasscutter.game.shop.ShopLimit;
import emu.grasscutter.game.tower.TowerData; import emu.grasscutter.game.tower.TowerData;
import emu.grasscutter.game.tower.TowerManager; import emu.grasscutter.game.tower.TowerManager;
@ -117,12 +116,13 @@ public class Player {
@Getter @Setter private int sceneId; @Getter @Setter private int sceneId;
@Getter @Setter private int regionId; @Getter @Setter private int regionId;
@Getter private int mainCharacterId; @Getter private int mainCharacterId;
@Setter private boolean godmode; // Getter is inGodmode @Getter @Setter private boolean inGodMode;
private boolean stamina; // Getter is getUnlimitedStamina, Setter is setUnlimitedStamina @Getter @Setter private boolean unlimitedStamina;
@Getter private Set<Integer> nameCardList; @Getter private Set<Integer> nameCardList;
@Getter private Set<Integer> flyCloakList; @Getter private Set<Integer> flyCloakList;
@Getter private Set<Integer> costumeList; @Getter private Set<Integer> costumeList;
@Getter private Set<Integer> personalLineList;
@Getter @Setter private Set<Integer> rewardedLevels; @Getter @Setter private Set<Integer> rewardedLevels;
@Getter @Setter private Set<Integer> homeRewardedLevels; @Getter @Setter private Set<Integer> homeRewardedLevels;
@Getter @Setter private Set<Integer> realmList; @Getter @Setter private Set<Integer> realmList;
@ -793,18 +793,6 @@ public class Player {
this.save(); this.save();
} }
public boolean getUnlimitedStamina() {
return stamina;
}
public void setUnlimitedStamina(boolean stamina) {
this.stamina = stamina;
}
public boolean inGodmode() {
return godmode;
}
public boolean hasSentLoginPackets() { public boolean hasSentLoginPackets() {
return hasSentLoginPackets; return hasSentLoginPackets;
} }

View File

@ -5,14 +5,18 @@ import emu.grasscutter.data.binout.ScenePointEntry;
import emu.grasscutter.data.excels.OpenStateData; import emu.grasscutter.data.excels.OpenStateData;
import emu.grasscutter.data.excels.OpenStateData.OpenStateCondType; import emu.grasscutter.data.excels.OpenStateData.OpenStateCondType;
import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.quest.enums.QuestCond;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.game.quest.enums.QuestState; import emu.grasscutter.game.quest.enums.QuestState;
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
import emu.grasscutter.server.packet.send.*; import emu.grasscutter.server.packet.send.*;
import lombok.val;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
// @Entity // @Entity
public class PlayerProgressManager extends BasePlayerDataManager { public final class PlayerProgressManager extends BasePlayerDataManager {
/****************************************************************************************************************** /******************************************************************************************************************
****************************************************************************************************************** ******************************************************************************************************************
* OPEN STATES * OPEN STATES
@ -145,6 +149,13 @@ public class PlayerProgressManager extends BasePlayerDataManager {
this.player.sendPacket(new PacketSetOpenStateRsp(openState, value)); this.player.sendPacket(new PacketSetOpenStateRsp(openState, value));
} }
/**
* This force sets an open state, ignoring all conditions and permissions
*/
public void forceSetOpenState(int openState, int value) {
this.setOpenState(openState, value);
}
/********** /**********
* Triggered unlocking of open states (unlock states whose conditions have been met.) * Triggered unlocking of open states (unlock states whose conditions have been met.)
**********/ **********/
@ -221,7 +232,7 @@ public class PlayerProgressManager extends BasePlayerDataManager {
// Fire quest trigger for trans point unlock. // Fire quest trigger for trans point unlock.
this.player this.player
.getQuestManager() .getQuestManager()
.triggerEvent(QuestContent.QUEST_CONTENT_UNLOCK_TRANS_POINT, sceneId, pointId); .queueEvent(QuestContent.QUEST_CONTENT_UNLOCK_TRANS_POINT, sceneId, pointId);
// Send packet. // Send packet.
this.player.sendPacket(new PacketScenePointUnlockNotify(sceneId, pointId)); this.player.sendPacket(new PacketScenePointUnlockNotify(sceneId, pointId));
@ -235,4 +246,35 @@ public class PlayerProgressManager extends BasePlayerDataManager {
// Send packet. // Send packet.
this.player.sendPacket(new PacketSceneAreaUnlockNotify(sceneId, areaId)); this.player.sendPacket(new PacketSceneAreaUnlockNotify(sceneId, areaId));
} }
/**
* Give replace costume to player (Amber, Jean, Mona, Rosaria)
*/
public void addReplaceCostumes(){
var currentPlayerCostumes = player.getCostumeList();
GameData.getAvatarReplaceCostumeDataMap().keySet().forEach(costumeId -> {
if (GameData.getAvatarCostumeDataMap().get(costumeId) == null || currentPlayerCostumes.contains(costumeId)){
return;
}
this.player.addCostume(costumeId);
});
}
/**
* Quest progress
*/
public void addQuestProgress(int id, int count){
var newCount = player.getPlayerProgress().addToCurrentProgress(id, count);
player.save();
player.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_ADD_QUEST_PROGRESS, id, newCount);
}
/**
* Item history
*/
public void addItemObtainedHistory(int id, int count){
var newCount = player.getPlayerProgress().addToItemHistory(id, count);
player.save();
player.getQuestManager().queueEvent(QuestCond.QUEST_COND_HISTORY_GOT_ANY_ITEM, id, newCount);
}
} }

View File

@ -40,7 +40,7 @@ import lombok.Setter;
import lombok.val; import lombok.val;
@Entity @Entity
public class TeamManager extends BasePlayerDataManager { public final class TeamManager extends BasePlayerDataManager {
@Transient private final List<EntityAvatar> avatars; @Transient private final List<EntityAvatar> avatars;
@Transient @Getter private final Set<EntityBaseGadget> gadgets; @Transient @Getter private final Set<EntityBaseGadget> gadgets;
@Transient @Getter private final IntSet teamResonances; @Transient @Getter private final IntSet teamResonances;
@ -658,6 +658,17 @@ public class TeamManager extends BasePlayerDataManager {
this.getPlayer().sendPacket(new PacketChangeAvatarRsp(guid)); this.getPlayer().sendPacket(new PacketChangeAvatarRsp(guid));
} }
/**
* Applies 10% of the avatar's max HP as damage.
* This occurs when the avatar is killed by the void.
*/
public void applyVoidDamage() {
this.getActiveTeam().forEach(entity -> {
entity.damage(entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * .1f);
player.sendPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar()));
});
}
public void onAvatarDie(long dieGuid) { public void onAvatarDie(long dieGuid) {
EntityAvatar deadAvatar = this.getCurrentAvatarEntity(); EntityAvatar deadAvatar = this.getCurrentAvatarEntity();

View File

@ -1,82 +1,36 @@
package emu.grasscutter.game.props; package emu.grasscutter.game.props;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.stream.Stream; import java.util.stream.Stream;
import emu.grasscutter.scripts.constants.IntValueEnum;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter; import lombok.Getter;
public enum ElementType { public enum ElementType implements IntValueEnum {
None (0, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY), None (0, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY),
Fire( Fire (1, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, 10101, "TeamResonance_Fire_Lv2", 1),
1, Water (2, FightProperty.FIGHT_PROP_CUR_WATER_ENERGY, FightProperty.FIGHT_PROP_MAX_WATER_ENERGY, 10201, "TeamResonance_Water_Lv2", 2),
FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, Grass (3, FightProperty.FIGHT_PROP_CUR_GRASS_ENERGY, FightProperty.FIGHT_PROP_MAX_GRASS_ENERGY, 10501, "TeamResonance_Grass_Lv2", 7),
FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, Electric (4, FightProperty.FIGHT_PROP_CUR_ELEC_ENERGY, FightProperty.FIGHT_PROP_MAX_ELEC_ENERGY, 10401, "TeamResonance_Electric_Lv2", 6),
10101, Ice (5, FightProperty.FIGHT_PROP_CUR_ICE_ENERGY, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY, 10601, "TeamResonance_Ice_Lv2", 4),
"TeamResonance_Fire_Lv2",
2),
Water(
2,
FightProperty.FIGHT_PROP_CUR_WATER_ENERGY,
FightProperty.FIGHT_PROP_MAX_WATER_ENERGY,
10201,
"TeamResonance_Water_Lv2",
3),
Grass(
3,
FightProperty.FIGHT_PROP_CUR_GRASS_ENERGY,
FightProperty.FIGHT_PROP_MAX_GRASS_ENERGY,
10501,
"TeamResonance_Grass_Lv2",
8),
Electric(
4,
FightProperty.FIGHT_PROP_CUR_ELEC_ENERGY,
FightProperty.FIGHT_PROP_MAX_ELEC_ENERGY,
10401,
"TeamResonance_Electric_Lv2",
7),
Ice(
5,
FightProperty.FIGHT_PROP_CUR_ICE_ENERGY,
FightProperty.FIGHT_PROP_MAX_ICE_ENERGY,
10601,
"TeamResonance_Ice_Lv2",
5),
Frozen (6, FightProperty.FIGHT_PROP_CUR_ICE_ENERGY, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY), Frozen (6, FightProperty.FIGHT_PROP_CUR_ICE_ENERGY, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY),
Wind( Wind (7, FightProperty.FIGHT_PROP_CUR_WIND_ENERGY, FightProperty.FIGHT_PROP_MAX_WIND_ENERGY, 10301, "TeamResonance_Wind_Lv2", 3),
7, Rock (8, FightProperty.FIGHT_PROP_CUR_ROCK_ENERGY, FightProperty.FIGHT_PROP_MAX_ROCK_ENERGY, 10701, "TeamResonance_Rock_Lv2", 5),
FightProperty.FIGHT_PROP_CUR_WIND_ENERGY,
FightProperty.FIGHT_PROP_MAX_WIND_ENERGY,
10301,
"TeamResonance_Wind_Lv2",
4),
Rock(
8,
FightProperty.FIGHT_PROP_CUR_ROCK_ENERGY,
FightProperty.FIGHT_PROP_MAX_ROCK_ENERGY,
10701,
"TeamResonance_Rock_Lv2",
6),
AntiFire (9, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY), AntiFire (9, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY),
Default( Default (255, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, 10801, "TeamResonance_AllDifferent");
255,
FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY,
FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY,
10801,
"TeamResonance_AllDifferent");
private static final Int2ObjectMap<ElementType> map = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<ElementType> map = new Int2ObjectOpenHashMap<>();
private static final Map<String, ElementType> stringMap = new HashMap<>(); private static final Map<String, ElementType> stringMap = new HashMap<>();
static { static {
Stream.of(values()) // Create bindings for each value.
.forEach( Stream.of(ElementType.values()).forEach(entry -> {
e -> { map.put(entry.getValue(), entry);
map.put(e.getValue(), e); stringMap.put(entry.name(), entry);
stringMap.put(e.name(), e);
}); });
} }
@ -84,34 +38,23 @@ public enum ElementType {
@Getter private final int teamResonanceId; @Getter private final int teamResonanceId;
@Getter private final FightProperty curEnergyProp; @Getter private final FightProperty curEnergyProp;
@Getter private final FightProperty maxEnergyProp; @Getter private final FightProperty maxEnergyProp;
@Getter private final int depotValue; @Getter private final int depotIndex;
@Getter private final int configHash; @Getter private final int configHash;
ElementType(int value, FightProperty curEnergyProp, FightProperty maxEnergyProp) { ElementType(int value, FightProperty curEnergyProp, FightProperty maxEnergyProp) {
this(value, curEnergyProp, maxEnergyProp, 0, null, 1); this(value, curEnergyProp, maxEnergyProp, 0, null, 1);
} }
ElementType( ElementType(int value, FightProperty curEnergyProp, FightProperty maxEnergyProp, int teamResonanceId, String configName) {
int value,
FightProperty curEnergyProp,
FightProperty maxEnergyProp,
int teamResonanceId,
String configName) {
this(value, curEnergyProp, maxEnergyProp, teamResonanceId, configName, 1); this(value, curEnergyProp, maxEnergyProp, teamResonanceId, configName, 1);
} }
ElementType( ElementType(int value, FightProperty curEnergyProp, FightProperty maxEnergyProp, int teamResonanceId, String configName, int depotIndex) {
int value,
FightProperty curEnergyProp,
FightProperty maxEnergyProp,
int teamResonanceId,
String configName,
int depotValue) {
this.value = value; this.value = value;
this.curEnergyProp = curEnergyProp; this.curEnergyProp = curEnergyProp;
this.maxEnergyProp = maxEnergyProp; this.maxEnergyProp = maxEnergyProp;
this.teamResonanceId = teamResonanceId; this.teamResonanceId = teamResonanceId;
this.depotValue = depotValue; this.depotIndex = depotIndex;
if (configName != null) { if (configName != null) {
this.configHash = Utils.abilityHash(configName); this.configHash = Utils.abilityHash(configName);
} else { } else {

View File

@ -1,10 +0,0 @@
package emu.grasscutter.game.quest;
import emu.grasscutter.game.quest.enums.QuestTrigger;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface QuestValue {
QuestTrigger value();
}

View File

@ -15,8 +15,9 @@ public class ConditionPersonalLineUnlock extends BaseCondition {
QuestData questData, QuestData questData,
QuestData.QuestAcceptCondition condition, QuestData.QuestAcceptCondition condition,
String paramStr, String paramStr,
int... params) { int... params
val personalLineId = condition.getParam()[0]; ) {
var personalLineId = condition.getParam()[0];
return owner.getPersonalLineList().contains(personalLineId); return owner.getPersonalLineList().contains(personalLineId);
} }
} }

View File

@ -12,6 +12,6 @@ public class ExecAddCurAvatarEnergy extends QuestExecHandler {
@Override @Override
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) { public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
Grasscutter.getLogger().info("Energy refilled"); Grasscutter.getLogger().info("Energy refilled");
return quest.getOwner().getEnergyManager().refillEntityAvatarEnergy(); return quest.getOwner().getEnergyManager().refillActiveEnergy();
} }
} }

View File

@ -8,7 +8,7 @@ import emu.grasscutter.game.quest.handlers.QuestExecHandler;
import java.util.Arrays; import java.util.Arrays;
@QuestValueExec(QuestExec.QUEST_EXEC_ADD_QUEST_PROGRESS) @QuestValueExec(QuestExec.QUEST_EXEC_ADD_QUEST_PROGRESS)
public class ExecAddQuestProgress extends QuestExecHandler { public final class ExecAddQuestProgress extends QuestExecHandler {
@Override @Override
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) { public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
var param = var param =

View File

@ -6,13 +6,12 @@ import emu.grasscutter.game.quest.QuestValueExec;
import emu.grasscutter.game.quest.enums.QuestExec; import emu.grasscutter.game.quest.enums.QuestExec;
import emu.grasscutter.game.quest.handlers.QuestExecHandler; import emu.grasscutter.game.quest.handlers.QuestExecHandler;
import java.util.Arrays; import java.util.Arrays;
import lombok.val;
@QuestValueExec(QuestExec.QUEST_EXEC_SET_OPEN_STATE) @QuestValueExec(QuestExec.QUEST_EXEC_SET_OPEN_STATE)
public class ExecSetOpenState extends QuestExecHandler { public class ExecSetOpenState extends QuestExecHandler {
@Override @Override
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) { public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
val param = var param =
Arrays.stream(paramStr).filter(i -> !i.isBlank()).mapToInt(Integer::parseInt).toArray(); Arrays.stream(paramStr).filter(i -> !i.isBlank()).mapToInt(Integer::parseInt).toArray();
quest.getOwner().getProgressManager().forceSetOpenState(param[0], param[1]); quest.getOwner().getProgressManager().forceSetOpenState(param[0], param[1]);

View File

@ -19,20 +19,23 @@ import emu.grasscutter.game.entity.gadget.GadgetWorktop;
import emu.grasscutter.game.managers.blossom.BlossomManager; import emu.grasscutter.game.managers.blossom.BlossomManager;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.player.TeamInfo; import emu.grasscutter.game.player.TeamInfo;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.*;
import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.quest.QuestGroupSuite; import emu.grasscutter.game.quest.QuestGroupSuite;
import emu.grasscutter.game.world.data.TeleportProperties;
import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult; import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
import emu.grasscutter.net.proto.EnterTypeOuterClass;
import emu.grasscutter.net.proto.SelectWorktopOptionReqOuterClass; import emu.grasscutter.net.proto.SelectWorktopOptionReqOuterClass;
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType; import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
import emu.grasscutter.scripts.SceneIndexManager; import emu.grasscutter.scripts.SceneIndexManager;
import emu.grasscutter.scripts.SceneScriptManager; import emu.grasscutter.scripts.SceneScriptManager;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneBlock; import emu.grasscutter.scripts.data.SceneBlock;
import emu.grasscutter.scripts.data.SceneGadget;
import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.event.player.PlayerTeleportEvent;
import emu.grasscutter.server.packet.send.*; import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.KahnsSort;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import java.util.*; import java.util.*;
@ -41,8 +44,9 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.val;
public class Scene { public final class Scene {
@Getter private final World world; @Getter private final World world;
@Getter private final SceneData sceneData; @Getter private final SceneData sceneData;
@Getter private final List<Player> players; @Getter private final List<Player> players;
@ -115,6 +119,13 @@ public class Scene {
.orElse(null); .orElse(null);
} }
public GameEntity getEntityByConfigId(int configId, int groupId) {
return this.entities.values().stream()
.filter(x -> x.getConfigId() == configId && x.getGroupId() == groupId)
.findFirst()
.orElse(null);
}
/** /**
* Sets the scene's pause state. Sends the current scene's time to all players. * Sets the scene's pause state. Sends the current scene's time to all players.
* *
@ -338,6 +349,7 @@ public class Scene {
public void handleAttack(AttackResult result) { public void handleAttack(AttackResult result) {
// GameEntity attacker = getEntityById(result.getAttackerId()); // GameEntity attacker = getEntityById(result.getAttackerId());
GameEntity target = getEntityById(result.getDefenseId()); GameEntity target = getEntityById(result.getDefenseId());
ElementType attackType = ElementType.getTypeByValue(result.getElementType());
if (target == null) { if (target == null) {
return; return;
@ -345,13 +357,13 @@ public class Scene {
// Godmode check // Godmode check
if (target instanceof EntityAvatar) { if (target instanceof EntityAvatar) {
if (((EntityAvatar) target).getPlayer().inGodmode()) { if (((EntityAvatar) target).getPlayer().isInGodMode()) {
return; return;
} }
} }
// Sanity check // Sanity check
target.damage(result.getDamage(), result.getAttackerId()); target.damage(result.getDamage(), result.getAttackerId(), attackType);
} }
public void killEntity(GameEntity target) { public void killEntity(GameEntity target) {
@ -399,17 +411,19 @@ public class Scene {
} }
public void onTick() { public void onTick() {
// disable script for home // Disable ticking for the player's home world.
if (this.getSceneType() == SceneType.SCENE_HOME_WORLD if (this.getSceneType() == SceneType.SCENE_HOME_WORLD
|| this.getSceneType() == SceneType.SCENE_HOME_ROOM) { || this.getSceneType() == SceneType.SCENE_HOME_ROOM) {
return; return;
} }
if (this.getScriptManager().isInit()) { if (this.getScriptManager().isInit()) {
this.checkBlocks(); this.checkBlocks();
} else { } else {
// TEMPORARY // TEMPORARY
this.checkSpawns(); this.checkSpawns();
} }
// Triggers // Triggers
this.scriptManager.checkRegions(); this.scriptManager.checkRegions();
@ -417,14 +431,134 @@ public class Scene {
challenge.onCheckTimeOut(); challenge.onCheckTimeOut();
} }
blossomManager.onTick(); this.blossomManager.onTick();
checkNpcGroup(); checkNpcGroup();
this.finishLoading();
this.checkPlayerRespawn();
if (this.tickCount++ % 10 == 0)
broadcastPacket(new PacketSceneTimeNotify(this));
}
/**
* Validates a player's current position.
* Teleports the player if the player is out of bounds.
*/
private void checkPlayerRespawn() {
var diePos = this.getScriptManager().getConfig().die_y;
// Check players in the scene.
this.players.forEach(player -> {
if (this.getScriptManager().getConfig() == null) return;
// Check if we need a respawn
if (diePos >= player.getPosition().getY()) {
//Respawn the player.
this.respawnPlayer(player);
}
});
// Check entities in the scene.
this.getEntities().forEach((id, entity) -> {
if (diePos >= entity.getPosition().getY()){
this.killEntity(entity);
}
});
}
/**
* @return The script's default location, or the player's location.
*/
public Position getDefaultLocation(Player player) {
val defaultPosition = getScriptManager().getConfig().born_pos;
return defaultPosition != null ? defaultPosition : player.getPosition();
}
/**
* @return The script's default rotation, or the player's rotation.
*/
private Position getDefaultRot(Player player) {
var defaultRotation = this.getScriptManager().getConfig().born_rot;
return defaultRotation != null ? defaultRotation : player.getRotation();
}
/**
* Gets the respawn position for the player.
*
* @param player The player to get the respawn position for.
* @return The respawn position for the player.
*/
private Position getRespawnLocation(Player player) {
// TODO: Get the last valid location the player stood on.
var lastCheckpointPos = dungeonManager != null ? dungeonManager.getRespawnLocation() : null;
return lastCheckpointPos != null ? lastCheckpointPos : getDefaultLocation(player);
}
/**
* Gets the respawn rotation for the player.
*
* @param player The player to get the respawn rotation for.
* @return The respawn rotation for the player.
*/
private Position getRespawnRotation(Player player) {
var lastCheckpointRot = this.dungeonManager != null ? this.dungeonManager.getRespawnRotation() : null;
return lastCheckpointRot != null ? lastCheckpointRot : this.getDefaultRot(player);
}
/**
* Teleports the player to the respawn location.
*
* @param player The player to respawn.
* @return true if the player was successfully respawned, false otherwise.
*/
public boolean respawnPlayer(Player player) {
// Apply void damage as a penalty.
player.getTeamManager().applyVoidDamage();
// TODO: Respawn the player at the last valid location.
var targetPos = getRespawnLocation(player);
var targetRot = getRespawnRotation(player);
var teleportProps = TeleportProperties.builder()
.sceneId(getId())
.teleportTo(targetPos)
.teleportRot(targetRot)
.teleportType(PlayerTeleportEvent.TeleportType.INTERNAL)
.enterType(EnterTypeOuterClass.EnterType.ENTER_TYPE_GOTO)
.enterReason(dungeonManager != null ? EnterReason.DungeonReviveOnWaypoint : EnterReason.Revival);
return this.getWorld().transferPlayerToScene(player, teleportProps.build());
}
/**
* Invoked when the scene finishes loading.
* Runs all callbacks that were added with {@link #runWhenFinished(Runnable)}.
*/
public void finishLoading() {
if (this.finishedLoading) return;
this.finishedLoading = true;
this.afterLoadedCallbacks.forEach(Runnable::run);
this.afterLoadedCallbacks.clear();
}
/**
* Adds a callback to be executed when the scene is finished loading.
* If the scene is already finished loading, the callback will be executed immediately.
*
* @param runnable The callback to be executed.
*/
public void runWhenFinished(Runnable runnable) {
if (this.isFinishedLoading()) {
runnable.run();return;
}
this.afterLoadedCallbacks.add(runnable);
} }
public int getEntityLevel(int baseLevel, int worldLevelOverride) { public int getEntityLevel(int baseLevel, int worldLevelOverride) {
int level = worldLevelOverride > 0 ? worldLevelOverride + baseLevel - 22 : baseLevel; int level = worldLevelOverride > 0 ? worldLevelOverride + baseLevel - 22 : baseLevel;
level = level >= 100 ? 100 : level; level = Math.min(level, 100);
level = level <= 0 ? 1 : level; level = level <= 0 ? 1 : level;
return level; return level;
@ -543,11 +677,12 @@ public class Scene {
} }
if (toAdd.size() > 0) { if (toAdd.size() > 0) {
toAdd.stream().forEach(this::addEntityDirectly); toAdd.forEach(this::addEntityDirectly);
this.broadcastPacket(new PacketSceneEntityAppearNotify(toAdd, VisionType.VISION_TYPE_BORN)); this.broadcastPacket(new PacketSceneEntityAppearNotify(toAdd, VisionType.VISION_TYPE_BORN));
} }
if (toRemove.size() > 0) { if (toRemove.size() > 0) {
toRemove.stream().forEach(this::removeEntityDirectly); toRemove.forEach(this::removeEntityDirectly);
this.broadcastPacket( this.broadcastPacket(
new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE)); new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE));
blossomManager.recycleGadgetEntity(toRemove); blossomManager.recycleGadgetEntity(toRemove);
@ -568,8 +703,9 @@ public class Scene {
return true; return true;
} }
private synchronized boolean loadBlock(SceneBlock block) { public synchronized boolean loadBlock(SceneBlock block) {
if (this.loadedBlocks.contains(block)) return false; if (this.loadedBlocks.contains(block)) return false;
this.onLoadBlock(block, this.players); this.onLoadBlock(block, this.players);
this.loadedBlocks.add(block); this.loadedBlocks.add(block);
return true; return true;
@ -578,7 +714,7 @@ public class Scene {
public synchronized void checkBlocks() { public synchronized void checkBlocks() {
Set<SceneBlock> visible = Set<SceneBlock> visible =
this.players.stream() this.players.stream()
.map(player -> this.getPlayerActiveBlocks(player)) .map(this::getPlayerActiveBlocks)
.flatMap(Collection::stream) .flatMap(Collection::stream)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
@ -635,11 +771,89 @@ public class Scene {
Grasscutter.getLogger().info("Scene {} Block {} loaded.", this.getId(), block.id); Grasscutter.getLogger().info("Scene {} Block {} loaded.", this.getId(), block.id);
} }
public int loadDynamicGroup(int group_id) {
SceneGroup group = getScriptManager().getGroupById(group_id);
if(group == null || getScriptManager().getGroupInstanceById(group_id) != null) return -1; //Group not found or already instanced
onLoadGroup(new ArrayList<>(List.of(group)));
if(GameData.getGroupReplacements().containsKey(group_id)) onRegisterGroups();
if (group.init_config == null) return -1;
return group.init_config.suite;
}
public boolean unregisterDynamicGroup(int groupId){
var group = getScriptManager().getGroupById(groupId);
if (group == null) return false;
var block = getScriptManager().getBlocks().get(group.block_id);
this.unloadGroup(block, groupId);
return true;
}
public void onRegisterGroups() {
var sceneGroups = this.loadedGroups;
var sceneGroupMap = sceneGroups.stream()
.collect(Collectors.toMap(item -> item.id, item -> item));
var sceneGroupsIds = sceneGroups.stream()
.map(group -> group.id)
.toList();
var dynamicGroups = sceneGroups.stream()
.filter(group -> group.dynamic_load)
.map(group -> group.id)
.toList();
//Create the graph
var nodes = new ArrayList<KahnsSort.Node>();
var groupList = new ArrayList<Integer>();
GameData.getGroupReplacements().values().stream().filter(replacement -> dynamicGroups.contains(replacement.id)).forEach(replacement -> {
Grasscutter.getLogger().info("Graph ordering replacement {}", replacement);
replacement.replace_groups.forEach(group -> {
nodes.add(new KahnsSort.Node(replacement.id, group));
if (!groupList.contains(group))
groupList.add(group);
});
if (!groupList.contains(replacement.id))
groupList.add(replacement.id);
});
KahnsSort.Graph graph = new KahnsSort.Graph(nodes, groupList);
List<Integer> dynamicGroupsOrdered = KahnsSort.doSort(graph);
if (dynamicGroupsOrdered == null) throw new RuntimeException("Invalid group replacement graph");
// Now we can start unloading and loading groups :D
dynamicGroupsOrdered.forEach(group -> {
if (GameData.getGroupReplacements().containsKey((int)group)) { //isGroupJoinReplacement
var data = GameData.getGroupReplacements().get((int)group);
var sceneGroupReplacement = this.loadedGroups.stream().filter(g -> g.id == group).findFirst().orElseThrow();
if (sceneGroupReplacement.is_replaceable != null) {
var it = data.replace_groups.iterator();
while (it.hasNext()) {
var replace_group = it.next();
if (!sceneGroupsIds.contains(replace_group)) continue;
// Check if we can replace this group
SceneGroup sceneGroup = sceneGroupMap.get(replace_group);
if (sceneGroup != null && sceneGroup.is_replaceable != null &&
((sceneGroup.is_replaceable.value &&
sceneGroup.is_replaceable.version <= sceneGroupReplacement.is_replaceable.version) ||
sceneGroup.is_replaceable.new_bin_only)) {
this.unloadGroup(scriptManager.getBlocks().get(sceneGroup.block_id), replace_group);
it.remove();
Grasscutter.getLogger().info("Graph ordering: unloaded {}", replace_group);
}
}
}
}
});
}
public void loadTriggerFromGroup(SceneGroup group, String triggerName) { public void loadTriggerFromGroup(SceneGroup group, String triggerName) {
// Load triggers and regions // Load triggers and regions
getScriptManager() this.getScriptManager().registerTrigger(group.triggers.values().stream()
.registerTrigger( .filter(p -> p.getName().contains(triggerName)).toList());
group.triggers.values().stream().filter(p -> p.name.contains(triggerName)).toList());
group.regions.values().stream() group.regions.values().stream()
.filter(q -> q.config_id == Integer.parseInt(triggerName.substring(13))) .filter(q -> q.config_id == Integer.parseInt(triggerName.substring(13)))
.map(region -> new EntityRegion(this, region)) .map(region -> new EntityRegion(this, region))
@ -650,50 +864,53 @@ public class Scene {
if (groups == null || groups.isEmpty()) { if (groups == null || groups.isEmpty()) {
return; return;
} }
for (SceneGroup group : groups) {
for (var group : groups) {
if(this.loadedGroups.contains(group)) continue;
// We load the script files for the groups here // We load the script files for the groups here
this.getScriptManager().loadGroupFromScript(group); this.getScriptManager().loadGroupFromScript(group);
if (!this.scriptManager.getLoadedGroupSetPerBlock().containsKey(group.block_id))
this.onLoadBlock(scriptManager.getBlocks().get(group.block_id), players);
this.scriptManager.getLoadedGroupSetPerBlock().get(group.block_id).add(group);
} }
// Spawn gadgets AFTER triggers are added // Spawn gadgets AFTER triggers are added
// TODO // TODO
var entities = new ArrayList<GameEntity>(); var entities = new ArrayList<GameEntity>();
for (SceneGroup group : groups) { for (var group : groups) {
if(this.loadedGroups.contains(group)) continue;
if (group.init_config == null) { if (group.init_config == null) {
continue; continue;
} }
var groupInstance = this.getScriptManager().getGroupInstanceById(group.id);
var cachedInstance = this.getScriptManager().getCachedGroupInstanceById(group.id);
if (cachedInstance != null) {
cachedInstance.setLuaGroup(group);
groupInstance = cachedInstance;
}
// Load garbages // Load garbages
List<SceneGadget> garbageGadgets = group.getGarbageGadgets(); var garbageGadgets = group.getGarbageGadgets();
if (garbageGadgets != null) { if (garbageGadgets != null) {
entities.addAll( entities.addAll(garbageGadgets.stream()
garbageGadgets.stream()
.map(g -> scriptManager.createGadget(group.id, group.block_id, g)) .map(g -> scriptManager.createGadget(group.id, group.block_id, g))
.filter(Objects::nonNull) .filter(Objects::nonNull).toList());
.toList());
} }
// Load suites // Load suites
int suite = group.init_config.suite; //int suite = group.findInitSuiteIndex(0);
this.getScriptManager().refreshGroup(groupInstance, 0, false); //This is what the official server does
if (suite == 0 || group.suites == null || group.suites.size() == 0) { this.loadedGroups.add(group);
continue;
} }
// just load the 'init' suite, avoid spawn the suite added by AddExtraGroupSuite etc. this.scriptManager.meetEntities(entities);
var suiteData = group.getSuiteByIndex(suite); groups.forEach(g -> scriptManager.callEvent(new ScriptArgs(g.id, EventType.EVENT_GROUP_LOAD, g.id)));
suiteData.sceneTriggers.forEach(getScriptManager()::registerTrigger);
entities.addAll(scriptManager.getGadgetsInGroupSuite(group, suiteData));
entities.addAll(scriptManager.getMonstersInGroupSuite(group, suiteData));
scriptManager.registerRegionInGroupSuite(group, suiteData);
}
scriptManager.meetEntities(entities);
// scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null);
// groups.forEach(g -> scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null));
Grasscutter.getLogger().info("Scene {} loaded {} group(s)", this.getId(), groups.size()); Grasscutter.getLogger().info("Scene {} loaded {} group(s)", this.getId(), groups.size());
} }
@ -718,6 +935,47 @@ public class Scene {
scriptManager.getLoadedGroupSetPerBlock().remove(block.id); scriptManager.getLoadedGroupSetPerBlock().remove(block.id);
Grasscutter.getLogger().info("Scene {} Block {} is unloaded.", this.getId(), block.id); Grasscutter.getLogger().info("Scene {} Block {} is unloaded.", this.getId(), block.id);
} }
/**
* Unloads a Lua group.
*
* @param block The block that contains the group.
* @param groupId The group ID.
*/
public void unloadGroup(SceneBlock block, int groupId) {
var toRemove = this.getEntities().values().stream()
.filter(e -> e != null && (
e.getBlockId() == block.id &&
e.getGroupId() == groupId)
).toList();
if (toRemove.size() > 0) {
toRemove.forEach(this::removeEntityDirectly);
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE));
}
var group = block.groups.get(groupId);
if (group.triggers != null) {
group.triggers.values().forEach(
this.getScriptManager()::deregisterTrigger);
}
if (group.regions != null) {
group.regions.values().forEach(
this.getScriptManager()::deregisterRegion);
}
this.scriptManager.getLoadedGroupSetPerBlock().get(block.id).remove(group);
this.loadedGroups.remove(group);
if (this.scriptManager.getLoadedGroupSetPerBlock().get(block.id).isEmpty()) {
this.scriptManager.getLoadedGroupSetPerBlock().remove(block.id);
Grasscutter.getLogger().info("Scene {} Block {} is unloaded.", this.getId(), block.id);
}
this.broadcastPacket(new PacketGroupUnloadNotify(List.of(groupId)));
this.scriptManager.unregisterGroup(group);
}
// Gadgets // Gadgets
public void onPlayerCreateGadget(EntityClientGadget gadget) { public void onPlayerCreateGadget(EntityClientGadget gadget) {
@ -738,7 +996,7 @@ public class Scene {
public void onPlayerDestroyGadget(int entityId) { public void onPlayerDestroyGadget(int entityId) {
GameEntity entity = getEntities().get(entityId); GameEntity entity = getEntities().get(entityId);
if (entity == null || !(entity instanceof EntityClientGadget gadget)) { if (!(entity instanceof EntityClientGadget gadget)) {
return; return;
} }
@ -834,20 +1092,40 @@ public class Scene {
return; return;
} }
sceneGroupSuite.forEach( sceneGroupSuite.forEach(i -> {
i -> {
var group = scriptManager.getGroupById(i.getGroup()); var group = scriptManager.getGroupById(i.getGroup());
if (group == null) { if (group == null) return;
return;
} var groupInstance = scriptManager.getGroupInstanceById(i.getGroup());
var suite = group.getSuiteByIndex(i.getSuite()); var suite = group.getSuiteByIndex(i.getSuite());
if (suite == null) { if (suite == null || groupInstance == null) {
return; return;
} }
scriptManager.addGroupSuite(group, suite);
scriptManager.refreshGroup(groupInstance, i.getSuite(), false);
}); });
} }
/**
* Adds an unlocked force to the scene.
*
* @param force The ID of the force to unlock.
*/
public void unlockForce(int force) {
this.unlockedForces.add(force);
this.broadcastPacket(new PacketSceneForceUnlockNotify(force, true));
}
/**
* Removes an unlocked force from the scene.
*
* @param force The ID of the force to lock.
*/
public void lockForce(int force) {
this.unlockedForces.remove(force);
this.broadcastPacket(new PacketSceneForceLockNotify(force));
}
public void selectWorktopOptionWith(SelectWorktopOptionReqOuterClass.SelectWorktopOptionReq req) { public void selectWorktopOptionWith(SelectWorktopOptionReqOuterClass.SelectWorktopOptionReq req) {
GameEntity entity = getEntityById(req.getGadgetEntityId()); GameEntity entity = getEntityById(req.getGadgetEntityId());
if (entity == null) { if (entity == null) {

View File

@ -1,5 +1,12 @@
package emu.grasscutter.game.world; package emu.grasscutter.game.world;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.bson.types.ObjectId;
import dev.morphia.annotations.Entity; import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id; import dev.morphia.annotations.Id;
import dev.morphia.annotations.Indexed; import dev.morphia.annotations.Indexed;
@ -7,16 +14,11 @@ import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.scripts.data.SceneGadget; import emu.grasscutter.scripts.data.SceneGadget;
import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneGroup;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.bson.types.ObjectId;
@Entity(value = "group_instances", useDiscriminator = false) @Entity(value = "group_instances", useDiscriminator = false)
public class SceneGroupInstance { public final class SceneGroupInstance {
@Id private ObjectId id; @Id private ObjectId id;
@Indexed private int ownerUid; //This group is owned by the host player @Indexed private int ownerUid; //This group is owned by the host player
@ -44,8 +46,7 @@ public class SceneGroupInstance {
this.cachedGadgetStates = new ConcurrentHashMap<>(); this.cachedGadgetStates = new ConcurrentHashMap<>();
this.cachedVariables = new ConcurrentHashMap<>(); this.cachedVariables = new ConcurrentHashMap<>();
this.isCached = this.isCached = false; //This is true when the group is not loaded on scene but caches suite data
false; // This is true when the group is not loaded on scene but caches suite data
} }
@Deprecated // Morphia only! @Deprecated // Morphia only!

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2,12 +2,14 @@ package emu.grasscutter.scripts;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import java.util.HashMap; import java.util.HashMap;
import emu.grasscutter.utils.Position;
import lombok.val;
import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
public class ScriptUtils { public interface ScriptUtils {
static HashMap<Object, Object> toMap(LuaTable table) {
public static HashMap<Object, Object> toMap(LuaTable table) {
HashMap<Object, Object> map = new HashMap<>(); HashMap<Object, Object> map = new HashMap<>();
LuaValue[] rootKeys = table.keys(); LuaValue[] rootKeys = table.keys();
for (LuaValue k : rootKeys) { for (LuaValue k : rootKeys) {
@ -20,7 +22,45 @@ public class ScriptUtils {
return map; return map;
} }
public static void print(LuaTable table) { static void print(LuaTable table) {
Grasscutter.getLogger().info(toMap(table).toString()); Grasscutter.getLogger().info(toMap(table).toString());
} }
/**
* Converts a position object into a Lua table.
*
* @param position The position object to convert.
* @return The Lua table.
*/
static LuaTable posToLua(Position position) {
var result = new LuaTable();
if (position != null) {
result.set("x", position.getX());
result.set("y", position.getY());
result.set("z", position.getZ());
} else {
result.set("x", 0);
result.set("y", 0);
result.set("z", 0);
}
return result;
}
/**
* Converts a Lua table into a position object.
*
* @param position The Lua table to convert.
* @return The position object.
*/
static Position luaToPos(LuaValue position) {
var result = new Position();
if (position != null && !position.isnil()) {
result.setX(position.get("x").optint(0));
result.setY(position.get("y").optint(0));
result.setZ(position.get("z").optint(0));
}
return result;
}
} }

View File

@ -12,4 +12,5 @@ public class SceneConfig {
public Position born_rot; public Position born_rot;
public Position begin_pos; public Position begin_pos;
public Position size; public Position size;
public float die_y;
} }

View File

@ -3,28 +3,33 @@ package emu.grasscutter.scripts.data;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.scripts.ScriptLoader; import emu.grasscutter.scripts.ScriptLoader;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import java.util.List; import lombok.Getter;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.script.Bindings;
import javax.script.CompiledScript;
import javax.script.ScriptException;
import lombok.Setter; import lombok.Setter;
import lombok.ToString; import lombok.ToString;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
import javax.script.Bindings;
import javax.script.CompiledScript;
import javax.script.ScriptException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.stream.Collectors;
@ToString @ToString
@Setter @Setter
public class SceneGroup { public final class SceneGroup {
public transient int public transient int block_id; // Not an actual variable in the scripts but we will keep it here for reference
block_id; // Not an actual variable in the scripts but we will keep it here for reference
public int id; public int id;
public int refresh_id; public int refresh_id;
public Position pos; public Position pos;
public Map<Integer, SceneMonster> monsters; // <ConfigId, Monster> public Map<Integer, SceneMonster> monsters; // <ConfigId, Monster>
public Map<Integer, SceneNPC> npcs; // <ConfigId, Npc>
public Map<Integer, SceneGadget> gadgets; // <ConfigId, Gadgets> public Map<Integer, SceneGadget> gadgets; // <ConfigId, Gadgets>
public Map<String, SceneTrigger> triggers; public Map<String, SceneTrigger> triggers;
public Map<Integer, SceneRegion> regions; public Map<Integer, SceneRegion> regions;
@ -34,11 +39,13 @@ public class SceneGroup {
public SceneBusiness business; public SceneBusiness business;
public SceneGarbage garbages; public SceneGarbage garbages;
public SceneInitConfig init_config; public SceneInitConfig init_config;
@Getter public boolean dynamic_load = false;
public SceneReplaceable is_replaceable;
private transient boolean loaded; // Not an actual variable in the scripts either private transient boolean loaded; // Not an actual variable in the scripts either
private transient CompiledScript script; private transient CompiledScript script;
private transient Bindings bindings; private transient Bindings bindings;
public static SceneGroup of(int groupId) { public static SceneGroup of(int groupId) {
var group = new SceneGroup(); var group = new SceneGroup();
group.id = groupId; group.id = groupId;
@ -66,6 +73,9 @@ public class SceneGroup {
} }
public SceneSuite getSuiteByIndex(int index) { public SceneSuite getSuiteByIndex(int index) {
if(index < 1 || index > suites.size()) {
return null;
}
return this.suites.get(index - 1); return this.suites.get(index - 1);
} }
@ -82,9 +92,7 @@ public class SceneGroup {
this.bindings = ScriptLoader.getEngine().createBindings(); this.bindings = ScriptLoader.getEngine().createBindings();
CompiledScript cs = CompiledScript cs = ScriptLoader.getScript("Scene/" + sceneId + "/scene" + sceneId + "_group" + this.id + ".lua");
ScriptLoader.getScript(
"Scene/" + sceneId + "/scene" + sceneId + "_group" + this.id + ".lua");
if (cs == null) { if (cs == null) {
return this; return this;
@ -97,74 +105,79 @@ public class SceneGroup {
cs.eval(this.bindings); cs.eval(this.bindings);
// Set // Set
this.monsters = this.monsters = ScriptLoader.getSerializer().toList(SceneMonster.class, this.bindings.get("monsters")).stream()
ScriptLoader.getSerializer()
.toList(SceneMonster.class, this.bindings.get("monsters"))
.stream()
.collect(Collectors.toMap(x -> x.config_id, y -> y, (a, b) -> a)); .collect(Collectors.toMap(x -> x.config_id, y -> y, (a, b) -> a));
this.monsters.values().forEach(m -> m.group = this); this.monsters.values().forEach(m -> m.group = this);
this.gadgets = this.npcs = ScriptLoader.getSerializer().toList(SceneNPC.class, this.bindings.get("npcs")).stream()
ScriptLoader.getSerializer() .collect(Collectors.toMap(x -> x.config_id, y -> y, (a, b) -> a));
.toList(SceneGadget.class, this.bindings.get("gadgets")) this.npcs.values().forEach(m -> m.group = this);
.stream()
this.gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, this.bindings.get("gadgets")).stream()
.collect(Collectors.toMap(x -> x.config_id, y -> y, (a, b) -> a)); .collect(Collectors.toMap(x -> x.config_id, y -> y, (a, b) -> a));
this.gadgets.values().forEach(m -> m.group = this); this.gadgets.values().forEach(m -> m.group = this);
this.triggers = this.triggers = ScriptLoader.getSerializer().toList(SceneTrigger.class, this.bindings.get("triggers")).stream()
ScriptLoader.getSerializer() .collect(Collectors.toMap(SceneTrigger::getName, y -> y, (a, b) -> a));
.toList(SceneTrigger.class, this.bindings.get("triggers"))
.stream()
.collect(Collectors.toMap(x -> x.name, y -> y, (a, b) -> a));
this.triggers.values().forEach(t -> t.currentGroup = this); this.triggers.values().forEach(t -> t.currentGroup = this);
this.suites = this.suites = ScriptLoader.getSerializer().toList(SceneSuite.class, this.bindings.get("suites"));
ScriptLoader.getSerializer().toList(SceneSuite.class, this.bindings.get("suites")); this.regions = ScriptLoader.getSerializer().toList(SceneRegion.class, this.bindings.get("regions")).stream()
this.regions =
ScriptLoader.getSerializer()
.toList(SceneRegion.class, this.bindings.get("regions"))
.stream()
.collect(Collectors.toMap(x -> x.config_id, y -> y, (a, b) -> a)); .collect(Collectors.toMap(x -> x.config_id, y -> y, (a, b) -> a));
this.regions.values().forEach(m -> m.group = this); this.regions.values().forEach(m -> m.group = this);
this.init_config = this.init_config = ScriptLoader.getSerializer().toObject(SceneInitConfig.class, this.bindings.get("init_config"));
ScriptLoader.getSerializer()
.toObject(SceneInitConfig.class, this.bindings.get("init_config"));
// Garbages // TODO: fix properly later // Garbages // TODO: fix properly later
Object garbagesValue = this.bindings.get("garbages"); Object garbagesValue = this.bindings.get("garbages");
if (garbagesValue instanceof LuaValue garbagesTable) { if (garbagesValue instanceof LuaValue garbagesTable) {
this.garbages = new SceneGarbage(); this.garbages = new SceneGarbage();
if (garbagesTable.checktable().get("gadgets") != LuaValue.NIL) { if (garbagesTable.checktable().get("gadgets") != LuaValue.NIL) {
this.garbages.gadgets = this.garbages.gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, garbagesTable.checktable().get("gadgets").checktable());
ScriptLoader.getSerializer()
.toList(
SceneGadget.class, garbagesTable.checktable().get("gadgets").checktable());
this.garbages.gadgets.forEach(m -> m.group = this); this.garbages.gadgets.forEach(m -> m.group = this);
} }
} }
// Add variables to suite // Add variables to suite
this.variables = this.variables = ScriptLoader.getSerializer().toList(SceneVar.class, this.bindings.get("variables"));
ScriptLoader.getSerializer().toList(SceneVar.class, this.bindings.get("variables"));
// Add monsters and gadgets to suite // Add monsters and gadgets to suite
this.suites.forEach(i -> i.init(this)); this.suites.forEach(i -> i.init(this));
} catch (ScriptException e) { } catch (ScriptException e) {
Grasscutter.getLogger() Grasscutter.getLogger().error("An error occurred while loading group " + this.id + " in scene " + sceneId + ".", e);
.error(
"An error occurred while loading group " + this.id + " in scene " + sceneId + ".", e);
} }
Grasscutter.getLogger().debug("Successfully loaded group {} in scene {}.", this.id, sceneId); Grasscutter.getLogger().debug("Successfully loaded group {} in scene {}.", this.id, sceneId);
return this; return this;
} }
public int findInitSuiteIndex(int exclude_index) { //TODO: Investigate end index
if (init_config == null) return 1;
if (init_config.io_type == 1) return init_config.suite; //IO TYPE FLOW
if (init_config.rand_suite) {
if (suites.size() == 1) {
return init_config.suite;
} else {
List<Integer> randSuiteList = new ArrayList<>();
for (int i = 0; i < suites.size(); i++) {
if (i == exclude_index) continue;
var suite = suites.get(i);
for(int j = 0; j < suite.rand_weight; j++) randSuiteList.add(i);
}
return randSuiteList.get(new Random().nextInt(randSuiteList.size()));
}
}
return init_config.suite;
}
public Optional<SceneBossChest> searchBossChestInGroup() { public Optional<SceneBossChest> searchBossChestInGroup() {
return this.gadgets.values().stream() return this.gadgets.values().stream()
.filter(g -> g.boss_chest != null && g.boss_chest.monster_config_id > 0) .filter(g -> g.boss_chest != null && g.boss_chest.monster_config_id > 0)
.map(g -> g.boss_chest) .map(g -> g.boss_chest)
.findFirst(); .findFirst();
} }
} }

View File

@ -5,8 +5,9 @@ import lombok.ToString;
@ToString @ToString
@Setter @Setter
public class SceneInitConfig { public final class SceneInitConfig {
public int suite; public int suite;
public int end_suite; public int end_suite;
public int io_type;
public boolean rand_suite; public boolean rand_suite;
} }

View File

@ -9,11 +9,7 @@ public class SceneMonster extends SceneObject {
public int monster_id; public int monster_id;
public int pose_id; public int pose_id;
public int drop_id; public int drop_id;
public int special_name_id;
public String drop_tag;
public int climate_area_id;
public boolean disableWander; public boolean disableWander;
public int title_id; public int title_id;
public int[] affix; public int special_name_id;
public int mark_flag;
} }

View File

@ -6,10 +6,11 @@ import lombok.ToString;
@ToString @ToString
@Setter @Setter
public class SceneObject { public abstract class SceneObject {
public int level; public int level;
public int config_id; public int config_id;
public int area_id; public int area_id;
public int vision_level = 0;
public Position pos; public Position pos;
public Position rot; public Position rot;

View File

@ -2,6 +2,7 @@ package emu.grasscutter.scripts.data;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import lombok.Setter; import lombok.Setter;
import lombok.ToString; import lombok.ToString;
@ -14,7 +15,8 @@ public class SceneSuite {
public List<String> triggers = List.of(); public List<String> triggers = List.of();
public List<Integer> regions = List.of(); public List<Integer> regions = List.of();
public int rand_weight; public int rand_weight;
public int[] npcs;
public boolean ban_refresh = false;
public transient List<SceneMonster> sceneMonsters = List.of(); public transient List<SceneMonster> sceneMonsters = List.of();
public transient List<SceneGadget> sceneGadgets = List.of(); public transient List<SceneGadget> sceneGadgets = List.of();
@ -22,39 +24,40 @@ public class SceneSuite {
public transient List<SceneRegion> sceneRegions = List.of(); public transient List<SceneRegion> sceneRegions = List.of();
public void init(SceneGroup sceneGroup) { public void init(SceneGroup sceneGroup) {
if (sceneGroup.monsters != null && this.monsters != null) { if(sceneGroup.monsters != null){
this.sceneMonsters = this.sceneMonsters = new ArrayList<>(
new ArrayList<>(
this.monsters.stream() this.monsters.stream()
.filter(sceneGroup.monsters::containsKey) .filter(sceneGroup.monsters::containsKey)
.map(sceneGroup.monsters::get) .map(sceneGroup.monsters::get)
.toList()); .toList()
);
} }
if (sceneGroup.gadgets != null && this.gadgets != null) { if(sceneGroup.gadgets != null){
this.sceneGadgets = this.sceneGadgets = new ArrayList<>(
new ArrayList<>(
this.gadgets.stream() this.gadgets.stream()
.filter(sceneGroup.gadgets::containsKey) .filter(sceneGroup.gadgets::containsKey)
.map(sceneGroup.gadgets::get) .map(sceneGroup.gadgets::get)
.toList()); .toList()
);
} }
if (sceneGroup.triggers != null && this.triggers != null) { if(sceneGroup.triggers != null) {
this.sceneTriggers = this.sceneTriggers = new ArrayList<>(
new ArrayList<>(
this.triggers.stream() this.triggers.stream()
.filter(sceneGroup.triggers::containsKey) .filter(sceneGroup.triggers::containsKey)
.map(sceneGroup.triggers::get) .map(sceneGroup.triggers::get)
.toList()); .toList()
);
} }
if (sceneGroup.regions != null && this.regions != null) { if(sceneGroup.regions != null) {
this.sceneRegions = this.sceneRegions = new ArrayList<>(
new ArrayList<>(
this.regions.stream() this.regions.stream()
.filter(sceneGroup.regions::containsKey) .filter(sceneGroup.regions::containsKey)
.map(sceneGroup.regions::get) .map(sceneGroup.regions::get)
.toList()); .toList()
} );
}
} }
} }

View File

@ -1,18 +1,20 @@
package emu.grasscutter.scripts.data; package emu.grasscutter.scripts.data;
import lombok.Setter; import lombok.*;
@Setter @Setter
public class SceneTrigger { @Getter
public String name; @NoArgsConstructor
public int config_id; // todo find way to deserialize from lua with final fields, maybe with the help of Builder?
public int event; public final class SceneTrigger {
public String source; private String name;
public String condition; private int config_id;
public String action; private int event;
public boolean forbid_guest; private int trigger_count = 1;
public int trigger_count; private String source;
public String tlog_tag; private String condition;
private String action;
private String tag;
public transient SceneGroup currentGroup; public transient SceneGroup currentGroup;
@ -20,8 +22,7 @@ public class SceneTrigger {
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj instanceof SceneTrigger sceneTrigger){ if (obj instanceof SceneTrigger sceneTrigger){
return this.name.equals(sceneTrigger.name); return this.name.equals(sceneTrigger.name);
} } else return super.equals(obj);
return super.equals(obj);
} }
@Override @Override
@ -31,29 +32,14 @@ public class SceneTrigger {
@Override @Override
public String toString() { public String toString() {
return "SceneTrigger{" return "SceneTrigger{" +
+ "name='" "name='" + name + '\'' +
+ name ", config_id=" + config_id +
+ '\'' ", event=" + event +
+ ", config_id=" ", source='" + source + '\'' +
+ config_id ", condition='" + condition + '\'' +
+ ", event=" ", action='" + action + '\'' +
+ event ", trigger_count='" + trigger_count + '\'' +
+ ", source='" '}';
+ source
+ '\''
+ ", condition='"
+ condition
+ '\''
+ ", action='"
+ action
+ '\''
+ ", forbid_guest='"
+ forbid_guest
+ '\''
+ ", trigger_count='"
+ trigger_count
+ '\''
+ '}';
} }
} }

View File

@ -6,16 +6,23 @@ public class ScriptArgs {
public int param3; public int param3;
public int source_eid; // Source entity public int source_eid; // Source entity
public int target_eid; public int target_eid;
public int group_id;
public String source; // source string, used for timers
public int type; // lua event type, used by scripts and the ScriptManager
public ScriptArgs() {} public ScriptArgs(int groupId, int eventType) {
this(groupId, eventType, 0,0);
public ScriptArgs(int param1) {
this.param1 = param1;
} }
public ScriptArgs(int param1, int param2) { public ScriptArgs(int groupId, int eventType, int param1) {
this(groupId, eventType, param1,0);
}
public ScriptArgs(int groupId, int eventType, int param1, int param2) {
this.type = eventType;
this.param1 = param1; this.param1 = param1;
this.param2 = param2; this.param2 = param2;
this.group_id = groupId;
} }
public int getParam1() { public int getParam1() {
@ -62,4 +69,22 @@ public class ScriptArgs {
this.target_eid = target_eid; this.target_eid = target_eid;
return this; return this;
} }
public String getEventSource() {
return source;
}
public ScriptArgs setEventSource(String source) {
this.source = source;
return this;
}
public int getGroupId() {
return group_id;
}
public ScriptArgs setGroupId(int group_id) {
this.group_id = group_id;
return this;
}
} }

View File

@ -7,11 +7,13 @@ import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.SceneMonster; import emu.grasscutter.scripts.data.SceneMonster;
import emu.grasscutter.scripts.data.ScriptArgs; import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.scripts.listener.ScriptMonsterListener; import emu.grasscutter.scripts.listener.ScriptMonsterListener;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
public class ScriptMonsterTideService { public final class ScriptMonsterTideService {
private final SceneScriptManager sceneScriptManager; private final SceneScriptManager sceneScriptManager;
private final SceneGroup currentGroup; private final SceneGroup currentGroup;
private final AtomicInteger monsterAlive; private final AtomicInteger monsterAlive;
@ -19,15 +21,12 @@ public class ScriptMonsterTideService {
private final AtomicInteger monsterKillCount; private final AtomicInteger monsterKillCount;
private final int monsterSceneLimit; private final int monsterSceneLimit;
private final ConcurrentLinkedQueue<Integer> monsterConfigOrders; private final ConcurrentLinkedQueue<Integer> monsterConfigOrders;
private final List<Integer> monsterConfigIds;
private final OnMonsterCreated onMonsterCreated= new OnMonsterCreated(); private final OnMonsterCreated onMonsterCreated= new OnMonsterCreated();
private final OnMonsterDead onMonsterDead= new OnMonsterDead(); private final OnMonsterDead onMonsterDead= new OnMonsterDead();
public ScriptMonsterTideService( public ScriptMonsterTideService(SceneScriptManager sceneScriptManager,
SceneScriptManager sceneScriptManager, SceneGroup group, int tideCount, int monsterSceneLimit, Integer[] ordersConfigId){
SceneGroup group,
int tideCount,
int monsterSceneLimit,
Integer[] ordersConfigId) {
this.sceneScriptManager = sceneScriptManager; this.sceneScriptManager = sceneScriptManager;
this.currentGroup = group; this.currentGroup = group;
this.monsterSceneLimit = monsterSceneLimit; this.monsterSceneLimit = monsterSceneLimit;
@ -35,15 +34,23 @@ public class ScriptMonsterTideService {
this.monsterKillCount = new AtomicInteger(0); this.monsterKillCount = new AtomicInteger(0);
this.monsterAlive = new AtomicInteger(0); this.monsterAlive = new AtomicInteger(0);
this.monsterConfigOrders = new ConcurrentLinkedQueue<>(List.of(ordersConfigId)); this.monsterConfigOrders = new ConcurrentLinkedQueue<>(List.of(ordersConfigId));
this.monsterConfigIds = List.of(ordersConfigId);
this.sceneScriptManager this.sceneScriptManager.getScriptMonsterSpawnService().addMonsterCreatedListener(onMonsterCreated);
.getScriptMonsterSpawnService()
.addMonsterCreatedListener(onMonsterCreated);
this.sceneScriptManager.getScriptMonsterSpawnService().addMonsterDeadListener(onMonsterDead); this.sceneScriptManager.getScriptMonsterSpawnService().addMonsterDeadListener(onMonsterDead);
// spawn the first turn // spawn the first turn
for (int i = 0; i < this.monsterSceneLimit; i++) { for (int i = 0; i < this.monsterSceneLimit; i++) {
sceneScriptManager.addEntity( sceneScriptManager.addEntity(this.sceneScriptManager.createMonster(group.id, group.block_id, getNextMonster()));
this.sceneScriptManager.createMonster(group.id, group.block_id, getNextMonster())); }
}
public class OnMonsterCreated implements ScriptMonsterListener{
@Override
public void onNotify(EntityMonster sceneMonster) {
if(monsterConfigIds.contains(sceneMonster.getConfigId()) && monsterSceneLimit > 0){
monsterAlive.incrementAndGet();
monsterTideCount.decrementAndGet();
}
} }
} }
@ -56,23 +63,6 @@ public class ScriptMonsterTideService {
return currentGroup.monsters.values().stream().findFirst().orElse(null); return currentGroup.monsters.values().stream().findFirst().orElse(null);
} }
public void unload() {
this.sceneScriptManager
.getScriptMonsterSpawnService()
.removeMonsterCreatedListener(onMonsterCreated);
this.sceneScriptManager.getScriptMonsterSpawnService().removeMonsterDeadListener(onMonsterDead);
}
public class OnMonsterCreated implements ScriptMonsterListener {
@Override
public void onNotify(EntityMonster sceneMonster) {
if (monsterSceneLimit > 0) {
monsterAlive.incrementAndGet();
monsterTideCount.decrementAndGet();
}
}
}
public class OnMonsterDead implements ScriptMonsterListener { public class OnMonsterDead implements ScriptMonsterListener {
@Override @Override
public void onNotify(EntityMonster sceneMonster) { public void onNotify(EntityMonster sceneMonster) {
@ -86,14 +76,17 @@ public class ScriptMonsterTideService {
monsterKillCount.incrementAndGet(); monsterKillCount.incrementAndGet();
if (monsterTideCount.get() > 0) { if (monsterTideCount.get() > 0) {
// add more // add more
sceneScriptManager.addEntity( sceneScriptManager.addEntity(sceneScriptManager.createMonster(currentGroup.id, currentGroup.block_id, getNextMonster()));
sceneScriptManager.createMonster(
currentGroup.id, currentGroup.block_id, getNextMonster()));
} }
// spawn the last turn of monsters // spawn the last turn of monsters
// fix the 5-2 // fix the 5-2
sceneScriptManager.callEvent( sceneScriptManager.callEvent(new ScriptArgs(currentGroup.id, EventType.EVENT_MONSTER_TIDE_DIE, monsterKillCount.get()));
EventType.EVENT_MONSTER_TIDE_DIE, new ScriptArgs(monsterKillCount.get())); }
}
}
public void unload(){
this.sceneScriptManager.getScriptMonsterSpawnService().removeMonsterCreatedListener(onMonsterCreated);
this.sceneScriptManager.getScriptMonsterSpawnService().removeMonsterDeadListener(onMonsterDead);
} }
} }

View File

@ -1,30 +1,24 @@
package emu.grasscutter.server.event.entity; package emu.grasscutter.server.event.entity;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.server.event.Cancellable; import emu.grasscutter.server.event.Cancellable;
import emu.grasscutter.server.event.types.EntityEvent; import emu.grasscutter.server.event.types.EntityEvent;
import lombok.Getter;
import lombok.Setter;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public final class EntityDamageEvent extends EntityEvent implements Cancellable { public final class EntityDamageEvent extends EntityEvent implements Cancellable {
@Nullable private final GameEntity damager; @Getter @Setter private float damage;
private float damage; @Getter @Setter private ElementType attackElementType;
@Getter @Nullable private final GameEntity damager;
public EntityDamageEvent(GameEntity entity, float damage, @Nullable GameEntity damager) { public EntityDamageEvent(GameEntity entity, float damage, ElementType attackElementType, @Nullable GameEntity damager) {
super(entity); super(entity);
this.damage = damage; this.damage = damage;
this.attackElementType = attackElementType;
this.damager = damager; this.damager = damager;
} }
public float getDamage() {
return this.damage;
}
public void setDamage(float damage) {
this.damage = damage;
}
@Nullable public GameEntity getDamager() {
return this.damager;
}
} }

View File

@ -13,7 +13,6 @@ import emu.grasscutter.game.chat.ChatSystemHandler;
import emu.grasscutter.game.combine.CombineManger; import emu.grasscutter.game.combine.CombineManger;
import emu.grasscutter.game.drop.DropSystem; import emu.grasscutter.game.drop.DropSystem;
import emu.grasscutter.game.dungeons.DungeonSystem; import emu.grasscutter.game.dungeons.DungeonSystem;
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
import emu.grasscutter.game.expedition.ExpeditionSystem; import emu.grasscutter.game.expedition.ExpeditionSystem;
import emu.grasscutter.game.gacha.GachaSystem; import emu.grasscutter.game.gacha.GachaSystem;
import emu.grasscutter.game.managers.cooking.CookingCompoundManager; import emu.grasscutter.game.managers.cooking.CookingCompoundManager;
@ -91,7 +90,6 @@ public final class GameServer extends KcpServer {
this.init(GameSessionManager.getListener(), channelConfig, address); this.init(GameSessionManager.getListener(), channelConfig, address);
DungeonChallenge.initialize();
EnergyManager.initialize(); EnergyManager.initialize();
StaminaManager.initialize(); StaminaManager.initialize();
CookingManager.initialize(); CookingManager.initialize();

View File

@ -1,41 +1,26 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.data.GameData; import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.data.excels.QuestData.QuestCondition;
import emu.grasscutter.game.quest.enums.QuestTrigger;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.AddQuestContentProgressReqOuterClass; import emu.grasscutter.net.proto.AddQuestContentProgressReqOuterClass.AddQuestContentProgressReq;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketAddQuestContentProgressRsp; import emu.grasscutter.server.packet.send.PacketAddQuestContentProgressRsp;
import java.util.List;
import java.util.stream.Stream;
@Opcodes(PacketOpcodes.AddQuestContentProgressReq) @Opcodes(PacketOpcodes.AddQuestContentProgressReq)
public class HandlerAddQuestContentProgressReq extends PacketHandler { public class HandlerAddQuestContentProgressReq extends PacketHandler {
@Override @Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
var req = AddQuestContentProgressReqOuterClass.AddQuestContentProgressReq.parseFrom(payload); var req = AddQuestContentProgressReq.parseFrom(payload);
// Find all conditions in quest that are the same as the given one // Find all conditions in quest that are the same as the given one
Stream<QuestCondition> finishCond = var type = QuestContent.getContentTriggerByValue(req.getContentType());
GameData.getQuestDataMap().get(req.getParam()).getFinishCond().stream(); if(type != null) {
Stream<QuestCondition> acceptCond = session.getPlayer().getQuestManager().queueEvent(type, req.getParam());
GameData.getQuestDataMap().get(req.getParam()).getAcceptCond().stream();
Stream<QuestCondition> failCond =
GameData.getQuestDataMap().get(req.getParam()).getFailCond().stream();
List<QuestCondition> allCondMatch =
Stream.concat(Stream.concat(acceptCond, failCond), finishCond)
.filter(p -> p.getType().getValue() == req.getContentType())
.toList();
for (QuestCondition cond : allCondMatch) {
session
.getPlayer()
.getQuestManager()
.triggerEvent(
QuestTrigger.getContentTriggerByValue(req.getContentType()), cond.getParam());
} }
session.send(new PacketAddQuestContentProgressRsp(req.getContentType())); session.send(new PacketAddQuestContentProgressRsp(req.getContentType()));
} }
} }

View File

@ -1,69 +1,39 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.GameConstants;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
import emu.grasscutter.data.excels.world.WorldAreaData;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.AvatarChangeElementTypeReqOuterClass.AvatarChangeElementTypeReq; import emu.grasscutter.net.proto.AvatarChangeElementTypeReqOuterClass.AvatarChangeElementTypeReq;
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketAbilityChangeNotify;
import emu.grasscutter.server.packet.send.PacketAvatarChangeElementTypeRsp; import emu.grasscutter.server.packet.send.PacketAvatarChangeElementTypeRsp;
import emu.grasscutter.server.packet.send.PacketAvatarFightPropNotify; import lombok.val;
import emu.grasscutter.server.packet.send.PacketAvatarSkillDepotChangeNotify;
/**
* Changes the currently active avatars Element if possible
*/
@Opcodes(PacketOpcodes.AvatarChangeElementTypeReq) @Opcodes(PacketOpcodes.AvatarChangeElementTypeReq)
public class HandlerAvatarChangeElementTypeReq extends PacketHandler { public class HandlerAvatarChangeElementTypeReq extends PacketHandler {
@Override @Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
AvatarChangeElementTypeReq req = AvatarChangeElementTypeReq.parseFrom(payload); var req = AvatarChangeElementTypeReq.parseFrom(payload);
var area = GameData.getWorldAreaDataMap().get(req.getAreaId());
WorldAreaData area = GameData.getWorldAreaDataMap().get(req.getAreaId()); if (area == null || area.getElementType() == null || area.getElementType().getDepotIndex() <= 0) {
if (area == null
|| area.getElementType() == null
|| area.getElementType().getDepotValue() <= 0) {
session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE)); session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE));
return; return;
} }
// Get current avatar, should be one of the main characters val avatar = session.getPlayer().getTeamManager().getCurrentAvatarEntity().getAvatar();
EntityAvatar mainCharacterEntity = if (!avatar.changeElement(area.getElementType())) {
session.getPlayer().getTeamManager().getCurrentAvatarEntity();
Avatar mainCharacter = mainCharacterEntity.getAvatar();
int skillDepotId = area.getElementType().getDepotValue();
switch (mainCharacter.getAvatarId()) {
case GameConstants.MAIN_CHARACTER_MALE -> skillDepotId += 500;
case GameConstants.MAIN_CHARACTER_FEMALE -> skillDepotId += 700;
default -> {
session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE)); session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE));
return; return;
} }
}
// Sanity checks for skill depots
AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(skillDepotId);
if (skillDepot == null || skillDepot.getId() == mainCharacter.getSkillDepotId()) {
session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE));
return;
}
// Set skill depot
mainCharacter.setSkillDepotData(skillDepot);
// Success // Success
session.send(new PacketAvatarChangeElementTypeRsp()); session.send(new PacketAvatarChangeElementTypeRsp());
}
// Ability change packet
session.send(new PacketAvatarSkillDepotChangeNotify(mainCharacter));
session.send(new PacketAbilityChangeNotify(mainCharacterEntity));
session.send(new PacketAvatarFightPropNotify(mainCharacter));
}
} }

View File

@ -116,7 +116,7 @@ public class HandlerCombatInvocationsNotify extends PacketHandler {
} }
private void handleFallOnGround(GameSession session, GameEntity entity, MotionState motionState) { private void handleFallOnGround(GameSession session, GameEntity entity, MotionState motionState) {
if (session.getPlayer().inGodmode()) { if (session.getPlayer().isInGodMode()) {
return; return;
} }
// People have reported that after plunge attack (client sends a FIGHT instead of // People have reported that after plunge attack (client sends a FIGHT instead of

View File

@ -1,5 +1,6 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
@ -23,6 +24,6 @@ public class HandlerEvtDoSkillSuccNotify extends PacketHandler {
// Handle skill notify in other managers. // Handle skill notify in other managers.
player.getStaminaManager().handleEvtDoSkillSuccNotify(session, skillId, casterId); player.getStaminaManager().handleEvtDoSkillSuccNotify(session, skillId, casterId);
player.getEnergyManager().handleEvtDoSkillSuccNotify(session, skillId, casterId); player.getEnergyManager().handleEvtDoSkillSuccNotify(session, skillId, casterId);
player.getQuestManager().triggerEvent(QuestContent.QUEST_CONTENT_SKILL, skillId, 0); player.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_SKILL, skillId, 0);
} }
} }

View File

@ -1,5 +1,6 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
@ -16,7 +17,7 @@ public class HandlerGadgetInteractReq extends PacketHandler {
session session
.getPlayer() .getPlayer()
.getQuestManager() .getQuestManager()
.triggerEvent(QuestContent.QUEST_CONTENT_INTERACT_GADGET, req.getGadgetId()); .queueEvent(QuestContent.QUEST_CONTENT_INTERACT_GADGET, req.getGadgetId());
session.getPlayer().interactWith(req.getGadgetEntityId(), req); session.getPlayer().interactWith(req.getGadgetEntityId(), req);
} }
} }

View File

@ -7,42 +7,41 @@ import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.MusicGameSettleReqOuterClass; import emu.grasscutter.net.proto.MusicGameSettleReqOuterClass.MusicGameSettleReq;
import emu.grasscutter.net.proto.RetcodeOuterClass;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketActivityInfoNotify; import emu.grasscutter.server.packet.send.PacketActivityInfoNotify;
import emu.grasscutter.server.packet.send.PacketMusicGameSettleRsp; import emu.grasscutter.server.packet.send.PacketMusicGameSettleRsp;
import lombok.val;
@Opcodes(PacketOpcodes.MusicGameSettleReq) @Opcodes(PacketOpcodes.MusicGameSettleReq)
public class HandlerMusicGameSettleReq extends PacketHandler { public class HandlerMusicGameSettleReq extends PacketHandler {
@Override @Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
var req = MusicGameSettleReqOuterClass.MusicGameSettleReq.parseFrom(payload); val req = MusicGameSettleReq.parseFrom(payload);
var playerData = val activityManager = session.getPlayer().getActivityManager();
session
.getPlayer() val playerDataOpt = activityManager.getPlayerActivityDataByActivityType(ActivityType.NEW_ACTIVITY_MUSIC_GAME);
.getActivityManager() if (playerDataOpt.isEmpty()) {
.getPlayerActivityDataByActivityType(ActivityType.NEW_ACTIVITY_MUSIC_GAME); session.send(new PacketMusicGameSettleRsp(RetcodeOuterClass.Retcode.RET_MUSIC_GAME_LEVEL_CONFIG_NOT_FOUND, req));
if (playerData.isEmpty()) {
return; return;
} }
var handler = (MusicGameActivityHandler) playerData.get().getActivityHandler();
boolean isNewRecord = false;
// check if custom beatmap
val playerData = playerDataOpt.get();
val handler = (MusicGameActivityHandler) playerData.getActivityHandler();
boolean isNewRecord = false;
// check if custom beatmap
if (req.getUgcGuid() == 0) { if (req.getUgcGuid() == 0) {
session session.getPlayer().getActivityManager().triggerWatcher(
.getPlayer()
.getActivityManager()
.triggerWatcher(
WatcherTriggerType.TRIGGER_FLEUR_FAIR_MUSIC_GAME_REACH_SCORE, WatcherTriggerType.TRIGGER_FLEUR_FAIR_MUSIC_GAME_REACH_SCORE,
String.valueOf(req.getMusicBasicId()), String.valueOf(req.getMusicBasicId()),
String.valueOf(req.getScore())); String.valueOf(req.getScore())
);
isNewRecord = isNewRecord = handler.setMusicGameRecord(playerData,
handler.setMusicGameRecord(
playerData.get(),
MusicGamePlayerData.MusicGameRecord.of() MusicGamePlayerData.MusicGameRecord.of()
.musicId(req.getMusicBasicId()) .musicId(req.getMusicBasicId())
.maxCombo(req.getMaxCombo()) .maxCombo(req.getMaxCombo())
@ -50,18 +49,18 @@ public class HandlerMusicGameSettleReq extends PacketHandler {
.build()); .build());
// update activity info // update activity info
session.send(new PacketActivityInfoNotify(handler.toProto(playerData.get()))); session.send(new PacketActivityInfoNotify(handler.toProto(playerData, activityManager.getConditionExecutor())));
} else { } else {
handler.setMusicGameCustomBeatmapRecord( handler.setMusicGameCustomBeatmapRecord(playerData,
playerData.get(),
MusicGamePlayerData.CustomBeatmapRecord.of() MusicGamePlayerData.CustomBeatmapRecord.of()
.musicShareId(req.getUgcGuid()) .musicShareId(req.getUgcGuid())
.score(req.getMaxCombo()) .score(req.getMaxCombo())
// .settle(req.getSuccess()) .settle(req.getIsSaveScore())
.build()); .build());
} }
session.send(
new PacketMusicGameSettleRsp(req.getMusicBasicId(), req.getUgcGuid(), isNewRecord)); session.send(new PacketMusicGameSettleRsp(req.getMusicBasicId(), req.getUgcGuid(), isNewRecord));
} }
} }

View File

@ -3,6 +3,7 @@ package emu.grasscutter.server.packet.recv;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.MainQuestData; import emu.grasscutter.data.binout.MainQuestData;
import emu.grasscutter.data.binout.MainQuestData.TalkData; import emu.grasscutter.data.binout.MainQuestData.TalkData;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
@ -49,16 +50,16 @@ public class HandlerNpcTalkReq extends PacketHandler {
session session
.getPlayer() .getPlayer()
.getQuestManager() .getQuestManager()
.triggerEvent( .queueEvent(
QuestContent.QUEST_CONTENT_COMPLETE_ANY_TALK, String.valueOf(req.getTalkId()), 0, 0); QuestContent.QUEST_CONTENT_COMPLETE_ANY_TALK, String.valueOf(req.getTalkId()), 0, 0);
session session
.getPlayer() .getPlayer()
.getQuestManager() .getQuestManager()
.triggerEvent(QuestContent.QUEST_CONTENT_COMPLETE_TALK, req.getTalkId(), 0); .queueEvent(QuestContent.QUEST_CONTENT_COMPLETE_TALK, req.getTalkId(), 0);
session session
.getPlayer() .getPlayer()
.getQuestManager() .getQuestManager()
.triggerEvent(QuestContent.QUEST_CONTENT_FINISH_PLOT, req.getTalkId(), 0); .queueEvent(QuestContent.QUEST_CONTENT_FINISH_PLOT, req.getTalkId(), 0);
} }
session.send(new PacketNpcTalkRsp(req.getNpcEntityId(), req.getTalkId(), req.getEntityId())); session.send(new PacketNpcTalkRsp(req.getNpcEntityId(), req.getTalkId(), req.getEntityId()));

View File

@ -1,6 +1,7 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.props.SceneType; import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
@ -16,7 +17,7 @@ public class HandlerPostEnterSceneReq extends PacketHandler {
session session
.getPlayer() .getPlayer()
.getQuestManager() .getQuestManager()
.triggerEvent(QuestContent.QUEST_CONTENT_ENTER_ROOM, session.getPlayer().getSceneId(), 0); .queueEvent(QuestContent.QUEST_CONTENT_ENTER_ROOM, session.getPlayer().getSceneId(), 0);
} }
session.send(new PacketPostEnterSceneRsp(session.getPlayer())); session.send(new PacketPostEnterSceneRsp(session.getPlayer()));

View File

@ -2,12 +2,13 @@ package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.SelectWorktopOptionReqOuterClass.SelectWorktopOptionReq; import emu.grasscutter.net.proto.SelectWorktopOptionReqOuterClass.SelectWorktopOptionReq;
import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.ScriptArgs; import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketSelectWorktopOptionRsp; import emu.grasscutter.server.packet.send.PacketSelectWorktopOptionRsp;
@ -25,16 +26,14 @@ public class HandlerSelectWorktopOptionReq extends PacketHandler {
return; return;
} }
session.getPlayer().getScene().selectWorktopOptionWith(req); session.getPlayer().getScene().selectWorktopOptionWith(req);
session session.getPlayer().getScene().getScriptManager().callEvent(
.getPlayer() new ScriptArgs(entity.getGroupId(), EventType.EVENT_SELECT_OPTION, entity.getConfigId(), req.getOptionId())
.getScene() );
.getScriptManager() session.getPlayer().getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_WORKTOP_SELECT, entity.getConfigId(), req.getOptionId());
.callEvent(
EventType.EVENT_SELECT_OPTION,
new ScriptArgs(entity.getConfigId(), req.getOptionId()));
} finally { } finally {
// Always send packet // Always send packet
session.send(new PacketSelectWorktopOptionRsp(req.getGadgetEntityId(), req.getOptionId())); session.send(new PacketSelectWorktopOptionRsp(req.getGadgetEntityId(), req.getOptionId()));
} }
} }
} }

View File

@ -14,7 +14,7 @@ public class HandlerSkipPlayerGameTimeReq extends PacketHandler {
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
var req = SkipPlayerGameTimeReqOuterClass.SkipPlayerGameTimeReq.parseFrom(payload); var req = SkipPlayerGameTimeReqOuterClass.SkipPlayerGameTimeReq.parseFrom(payload);
var player = session.getPlayer(); var player = session.getPlayer();
player.getScene().setTime(req.getGameTime()); player.updatePlayerGameTime(req.getGameTime());
player.getScene().broadcastPacket(new PacketPlayerGameTimeNotify(player)); player.getScene().broadcastPacket(new PacketPlayerGameTimeNotify(player));
player.sendPacket(new PacketSkipPlayerGameTimeRsp(req)); player.sendPacket(new PacketSkipPlayerGameTimeRsp(req));
} }

View File

@ -1,34 +1,27 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.entity.platform.EntityPlatform; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.gadget.platform.AbilityRoute;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.UpdateAbilityCreatedMovingPlatformNotifyOuterClass; import emu.grasscutter.net.proto.UpdateAbilityCreatedMovingPlatformNotifyOuterClass.UpdateAbilityCreatedMovingPlatformNotify;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketPlatformStartRouteNotify;
import emu.grasscutter.server.packet.send.PacketPlatformStopRouteNotify;
@Opcodes(PacketOpcodes.UpdateAbilityCreatedMovingPlatformNotify) @Opcodes(PacketOpcodes.UpdateAbilityCreatedMovingPlatformNotify)
public class HandlerUpdateAbilityCreatedMovingPlatformNotify extends PacketHandler { public class HandlerUpdateAbilityCreatedMovingPlatformNotify extends PacketHandler {
@Override @Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
var notify = var notify = UpdateAbilityCreatedMovingPlatformNotify.parseFrom(payload);
UpdateAbilityCreatedMovingPlatformNotifyOuterClass.UpdateAbilityCreatedMovingPlatformNotify
.parseFrom(payload);
var entity = session.getPlayer().getScene().getEntityById(notify.getEntityId()); var entity = session.getPlayer().getScene().getEntityById(notify.getEntityId());
if (!(entity instanceof EntityPlatform)) { if (!(entity instanceof EntityGadget entityGadget) || !(entityGadget.getRouteConfig() instanceof AbilityRoute)) {
return; return;
} }
var scene = ((EntityPlatform) entity).getOwner().getScene();
switch (notify.getOpType()) { switch (notify.getOpType()) {
case OP_TYPE_ACTIVATE -> scene.broadcastPacket( case OP_TYPE_ACTIVATE -> entityGadget.startPlatform();
new PacketPlatformStartRouteNotify((EntityPlatform) entity, scene)); case OP_TYPE_DEACTIVATE -> entityGadget.stopPlatform();
case OP_TYPE_DEACTIVATE -> scene.broadcastPacket(
new PacketPlatformStopRouteNotify((EntityPlatform) entity, scene));
} }
} }
} }

View File

@ -11,7 +11,7 @@ public class PacketChangeGameTimeRsp extends BasePacket {
super(PacketOpcodes.ChangeGameTimeRsp); super(PacketOpcodes.ChangeGameTimeRsp);
ChangeGameTimeRsp proto = ChangeGameTimeRsp proto =
ChangeGameTimeRsp.newBuilder().setCurGameTime(player.getScene().getTime()).build(); ChangeGameTimeRsp.newBuilder().setCurGameTime(player.getWorld().getGameTime()).build();
this.setData(proto); this.setData(proto);
} }

View File

@ -1,64 +1,13 @@
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge; import emu.grasscutter.game.dungeons.dungeon_results.BaseDungeonResult;
import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.DungeonSettleNotifyOuterClass.DungeonSettleNotify;
import emu.grasscutter.net.proto.ItemParamOuterClass;
import emu.grasscutter.net.proto.TowerLevelEndNotifyOuterClass.TowerLevelEndNotify;
public class PacketDungeonSettleNotify extends BasePacket { public class PacketDungeonSettleNotify extends BasePacket {
public PacketDungeonSettleNotify(BaseDungeonResult result) {
public PacketDungeonSettleNotify(WorldChallenge challenge) {
super(PacketOpcodes.DungeonSettleNotify); super(PacketOpcodes.DungeonSettleNotify);
DungeonSettleNotify proto = this.setData(result.getProto());
DungeonSettleNotify.newBuilder()
.setDungeonId(challenge.getScene().getDungeonData().getId())
.setIsSuccess(challenge.isSuccess())
.setCloseTime(challenge.getScene().getAutoCloseTime())
.setResult(challenge.isSuccess() ? 1 : 0)
.build();
this.setData(proto);
}
public PacketDungeonSettleNotify(
WorldChallenge challenge, boolean canJump, boolean hasNextLevel, int nextFloorId) {
super(PacketOpcodes.DungeonSettleNotify);
var continueStatus =
TowerLevelEndNotify.ContinueStateType.CONTINUE_STATE_TYPE_CAN_NOT_CONTINUE_VALUE;
if (challenge.isSuccess() && canJump) {
continueStatus =
hasNextLevel
? TowerLevelEndNotify.ContinueStateType.CONTINUE_STATE_TYPE_CAN_ENTER_NEXT_LEVEL_VALUE
: TowerLevelEndNotify.ContinueStateType
.CONTINUE_STATE_TYPE_CAN_ENTER_NEXT_FLOOR_VALUE;
}
var towerLevelEndNotify =
TowerLevelEndNotify.newBuilder()
.setIsSuccess(challenge.isSuccess())
.setContinueState(continueStatus)
.addFinishedStarCondList(1)
.addFinishedStarCondList(2)
.addFinishedStarCondList(3)
.addRewardItemList(
ItemParamOuterClass.ItemParam.newBuilder().setItemId(201).setCount(1000).build());
if (nextFloorId > 0 && canJump) {
towerLevelEndNotify.setNextFloorId(nextFloorId);
}
DungeonSettleNotify proto =
DungeonSettleNotify.newBuilder()
.setDungeonId(challenge.getScene().getDungeonData().getId())
.setIsSuccess(challenge.isSuccess())
.setCloseTime(challenge.getScene().getAutoCloseTime())
.setResult(challenge.isSuccess() ? 1 : 0)
.setTowerLevelEndNotify(towerLevelEndNotify.build())
.build();
this.setData(proto);
} }
} }

View File

@ -6,8 +6,9 @@ import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.EntityFightPropUpdateNotifyOuterClass.EntityFightPropUpdateNotify; import emu.grasscutter.net.proto.EntityFightPropUpdateNotifyOuterClass.EntityFightPropUpdateNotify;
public class PacketEntityFightPropUpdateNotify extends BasePacket { import java.util.Collection;
public class PacketEntityFightPropUpdateNotify extends BasePacket {
public PacketEntityFightPropUpdateNotify(GameEntity entity, FightProperty prop) { public PacketEntityFightPropUpdateNotify(GameEntity entity, FightProperty prop) {
super(PacketOpcodes.EntityFightPropUpdateNotify); super(PacketOpcodes.EntityFightPropUpdateNotify);
@ -19,4 +20,14 @@ public class PacketEntityFightPropUpdateNotify extends BasePacket {
this.setData(proto); this.setData(proto);
} }
public PacketEntityFightPropUpdateNotify(GameEntity entity, Collection<FightProperty> props) {
super(PacketOpcodes.EntityFightPropUpdateNotify);
var protoBuilder = EntityFightPropUpdateNotify.newBuilder()
.setEntityId(entity.getId());
props.forEach(p -> protoBuilder.putFightPropMap(p.getId(), entity.getFightProperty(p)));
this.setData(protoBuilder);
}
} }

View File

@ -17,7 +17,7 @@ public class PacketFinishedParentQuestNotify extends BasePacket {
for (GameMainQuest mainQuest : player.getQuestManager().getMainQuests().values()) { for (GameMainQuest mainQuest : player.getQuestManager().getMainQuests().values()) {
//Canceled Quests do not appear in this packet //Canceled Quests do not appear in this packet
if (mainQuest.getState() != ParentQuestState.PARENT_QUEST_STATE_CANCELED) { if (mainQuest.getState() != ParentQuestState.PARENT_QUEST_STATE_CANCELED) {
proto.addParentQuestList(mainQuest.toProto()); proto.addParentQuestList(mainQuest.toProto(false));
} }
} }

View File

@ -4,6 +4,7 @@ import emu.grasscutter.game.quest.GameMainQuest;
import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.FinishedParentQuestUpdateNotifyOuterClass.FinishedParentQuestUpdateNotify; import emu.grasscutter.net.proto.FinishedParentQuestUpdateNotifyOuterClass.FinishedParentQuestUpdateNotify;
import java.util.List; import java.util.List;
public class PacketFinishedParentQuestUpdateNotify extends BasePacket { public class PacketFinishedParentQuestUpdateNotify extends BasePacket {
@ -11,8 +12,9 @@ public class PacketFinishedParentQuestUpdateNotify extends BasePacket {
public PacketFinishedParentQuestUpdateNotify(GameMainQuest quest) { public PacketFinishedParentQuestUpdateNotify(GameMainQuest quest) {
super(PacketOpcodes.FinishedParentQuestUpdateNotify); super(PacketOpcodes.FinishedParentQuestUpdateNotify);
FinishedParentQuestUpdateNotify proto = FinishedParentQuestUpdateNotify proto = FinishedParentQuestUpdateNotify.newBuilder()
FinishedParentQuestUpdateNotify.newBuilder().addParentQuestList(quest.toProto()).build(); .addParentQuestList(quest.toProto(true))
.build();
this.setData(proto); this.setData(proto);
} }
@ -23,7 +25,7 @@ public class PacketFinishedParentQuestUpdateNotify extends BasePacket {
var proto = FinishedParentQuestUpdateNotify.newBuilder(); var proto = FinishedParentQuestUpdateNotify.newBuilder();
for (GameMainQuest mainQuest : quests) { for (GameMainQuest mainQuest : quests) {
proto.addParentQuestList(mainQuest.toProto()); proto.addParentQuestList(mainQuest.toProto(true));
} }
proto.build(); proto.build();
this.setData(proto); this.setData(proto);

View File

@ -1,11 +1,11 @@
package emu.grasscutter.server.packet.send; //package emu.grasscutter.server.packet.send;
//
import emu.grasscutter.net.packet.BasePacket; //import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes; //import emu.grasscutter.net.packet.PacketOpcodes;
//
public class PacketHomeUnknown2Rsp extends BasePacket { //public class PacketHomeUnknown2Rsp extends BasePacket {
//
public PacketHomeUnknown2Rsp() { // public PacketHomeUnknown2Rsp() {
super(PacketOpcodes.Unk2700_KIIOGMKFNNP_ServerRsp); // super(PacketOpcodes.Unk2700_KIIOGMKFNNP_ServerRsp);
} // }
} //}

View File

@ -2,7 +2,9 @@ package emu.grasscutter.server.packet.send;
import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.MusicGameSettleReqOuterClass;
import emu.grasscutter.net.proto.MusicGameSettleRspOuterClass; import emu.grasscutter.net.proto.MusicGameSettleRspOuterClass;
import emu.grasscutter.net.proto.RetcodeOuterClass;
public class PacketMusicGameSettleRsp extends BasePacket { public class PacketMusicGameSettleRsp extends BasePacket {
@ -13,6 +15,18 @@ public class PacketMusicGameSettleRsp extends BasePacket {
proto.setMusicBasicId(musicBasicId).setUgcGuid(musicShareId).setIsNewRecord(isNewRecord); proto.setMusicBasicId(musicBasicId).setUgcGuid(musicShareId).setIsNewRecord(isNewRecord);
this.setData(proto);
}
public PacketMusicGameSettleRsp(RetcodeOuterClass.Retcode errorCode, MusicGameSettleReqOuterClass.MusicGameSettleReq req) {
super(PacketOpcodes.MusicGameSettleRsp);
var proto = MusicGameSettleRspOuterClass.MusicGameSettleRsp.newBuilder()
.setRetcode(errorCode.getNumber())
.setMusicBasicId(req.getMusicBasicId())
.setUgcGuid(req.getUgcGuid());
this.setData(proto); this.setData(proto);
} }
} }

View File

@ -1,21 +1,19 @@
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.entity.platform.EntityPlatform; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.PlatformStartRouteNotifyOuterClass; import emu.grasscutter.net.proto.PlatformStartRouteNotifyOuterClass.PlatformStartRouteNotify;
import lombok.val;
public class PacketPlatformStartRouteNotify extends BasePacket { public class PacketPlatformStartRouteNotify extends BasePacket {
public PacketPlatformStartRouteNotify(EntityPlatform entity, Scene scene) { public PacketPlatformStartRouteNotify(EntityGadget gadgetEntity) {
super(PacketOpcodes.PlatformStartRouteNotify); super(PacketOpcodes.PlatformStartRouteNotify);
var notify = val notify = PlatformStartRouteNotify.newBuilder()
PlatformStartRouteNotifyOuterClass.PlatformStartRouteNotify.newBuilder() .setEntityId(gadgetEntity.getId())
.setEntityId(entity.getId()) .setSceneTime(gadgetEntity.getScene().getSceneTime())
.setSceneTime(scene.getSceneTime()) .setPlatform(gadgetEntity.getPlatformInfo());
.setPlatform(entity.onStartRoute())
.build();
this.setData(notify); this.setData(notify);
} }

View File

@ -1,20 +1,18 @@
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.entity.platform.EntityPlatform; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.PlatformStopRouteNotifyOuterClass; import emu.grasscutter.net.proto.PlatformStopRouteNotifyOuterClass;
public class PacketPlatformStopRouteNotify extends BasePacket { public class PacketPlatformStopRouteNotify extends BasePacket {
public PacketPlatformStopRouteNotify(EntityPlatform entity, Scene scene) { public PacketPlatformStopRouteNotify(EntityGadget gadgetEntity) {
super(PacketOpcodes.PlatformStopRouteNotify); super(PacketOpcodes.PlatformStopRouteNotify);
var notify = var notify = PlatformStopRouteNotifyOuterClass.PlatformStopRouteNotify.newBuilder()
PlatformStopRouteNotifyOuterClass.PlatformStopRouteNotify.newBuilder() .setPlatform(gadgetEntity.getPlatformInfo())
.setPlatform(entity.onStopRoute()) .setSceneTime(gadgetEntity.getScene().getSceneTime())
.setSceneTime(scene.getSceneTime()) .setEntityId(gadgetEntity.getId())
.setEntityId(entity.getId())
.build(); .build();
this.setData(notify); this.setData(notify);

View File

@ -12,7 +12,7 @@ public class PacketPlayerGameTimeNotify extends BasePacket {
PlayerGameTimeNotify proto = PlayerGameTimeNotify proto =
PlayerGameTimeNotify.newBuilder() PlayerGameTimeNotify.newBuilder()
.setGameTime(player.getScene().getTime()) .setGameTime(player.getWorld().getGameTime())
.setUid(player.getUid()) .setUid(player.getUid())
.build(); .build();

View File

@ -344,7 +344,7 @@ public final class Language {
"Cache modified %d, textmap modified %d".formatted(cacheModified, textmapsModified)); "Cache modified %d, textmap modified %d".formatted(cacheModified, textmapsModified));
if (textmapsModified < cacheModified) { if (textmapsModified < cacheModified) {
// Try loading from cache // Try loading from cache
Grasscutter.getLogger().info("Loading cached 'TextMaps'..."); Grasscutter.getLogger().debug("Loading cached 'TextMaps'...");
textMapStrings = loadTextMapsCache(); textMapStrings = loadTextMapsCache();
return; return;
} }