Format code

This commit is contained in:
KingRainbow44 2023-04-02 21:34:07 -04:00
parent b03870ab48
commit a3970f8e43
No known key found for this signature in database
GPG Key ID: FC2CB64B00D257BE
104 changed files with 15623 additions and 15004 deletions

View File

@ -1,139 +1,139 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.command.CommandHelpers.*; import static emu.grasscutter.command.CommandHelpers.*;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.entity.*; import 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.ElementType;
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.server.event.entity.EntityDamageEvent; import emu.grasscutter.server.event.entity.EntityDamageEvent;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import lombok.Setter; import lombok.Setter;
@Command( @Command(
label = "entity", label = "entity",
usage = { usage = {
"<configId gadget> [state<state>] [maxhp<maxhp>] [hp<hp>(0 for infinite)] [atk<atk>] [def<def>]", "<configId gadget> [state<state>] [maxhp<maxhp>] [hp<hp>(0 for infinite)] [atk<atk>] [def<def>]",
"<configId monster> [ai<aiId>] [maxhp<maxhp>] [hp<hp>(0 for infinite)] [atk<atk>] [def<def>]" "<configId monster> [ai<aiId>] [maxhp<maxhp>] [hp<hp>(0 for infinite)] [atk<atk>] [def<def>]"
}, },
permission = "server.entity") permission = "server.entity")
public final class EntityCommand implements CommandHandler { public final class EntityCommand implements CommandHandler {
private static final Map<Pattern, BiConsumer<EntityParameters, Integer>> intCommandHandlers = private static final Map<Pattern, BiConsumer<EntityParameters, Integer>> intCommandHandlers =
Map.ofEntries( Map.ofEntries(
Map.entry(stateRegex, EntityParameters::setState), Map.entry(stateRegex, EntityParameters::setState),
Map.entry(maxHPRegex, EntityParameters::setMaxHP), Map.entry(maxHPRegex, EntityParameters::setMaxHP),
Map.entry(hpRegex, EntityParameters::setHp), Map.entry(hpRegex, EntityParameters::setHp),
Map.entry(defRegex, EntityParameters::setDef), Map.entry(defRegex, EntityParameters::setDef),
Map.entry(atkRegex, EntityParameters::setAtk), Map.entry(atkRegex, EntityParameters::setAtk),
Map.entry(aiRegex, EntityParameters::setAi)); Map.entry(aiRegex, EntityParameters::setAi));
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
EntityParameters param = new EntityParameters(); EntityParameters param = new EntityParameters();
parseIntParameters(args, param, intCommandHandlers); parseIntParameters(args, param, intCommandHandlers);
// At this point, first remaining argument MUST be the id and the rest the pos // At this point, first remaining argument MUST be the id and the rest the pos
if (args.size() != 1) { if (args.size() != 1) {
sendUsageMessage(sender); // Reachable if someone does `/give lv90` or similar sendUsageMessage(sender); // Reachable if someone does `/give lv90` or similar
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
try { try {
param.configId = Integer.parseInt(args.get(0)); param.configId = Integer.parseInt(args.get(0));
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.cfgId")); CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.cfgId"));
} }
param.scene = targetPlayer.getScene(); param.scene = targetPlayer.getScene();
var entity = param.scene.getEntityByConfigId(param.configId); var entity = param.scene.getEntityByConfigId(param.configId);
if (entity == null) { if (entity == null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.entity.not_found_error")); CommandHandler.sendMessage(sender, translate(sender, "commands.entity.not_found_error"));
return; return;
} }
applyFightProps(entity, param); applyFightProps(entity, param);
applyGadgetParams(entity, param); applyGadgetParams(entity, param);
applyMonsterParams(entity, param); applyMonsterParams(entity, param);
CommandHandler.sendMessage(sender, translate(sender, "commands.status.success")); CommandHandler.sendMessage(sender, translate(sender, "commands.status.success"));
} }
private void applyGadgetParams(GameEntity entity, EntityParameters param) { private void applyGadgetParams(GameEntity entity, EntityParameters param) {
if (!(entity instanceof EntityGadget)) { if (!(entity instanceof EntityGadget)) {
return; return;
} }
if (param.state != -1) { if (param.state != -1) {
((EntityGadget) entity).updateState(param.state); ((EntityGadget) entity).updateState(param.state);
} }
} }
private void applyMonsterParams(GameEntity entity, EntityParameters param) { private void applyMonsterParams(GameEntity entity, EntityParameters param) {
if (!(entity instanceof EntityMonster)) { if (!(entity instanceof EntityMonster)) {
return; return;
} }
if (param.ai != -1) { if (param.ai != -1) {
((EntityMonster) entity).setAiId(param.ai); ((EntityMonster) entity).setAiId(param.ai);
// TODO notify // TODO notify
} }
} }
private void applyFightProps(GameEntity entity, EntityParameters param) { private void applyFightProps(GameEntity entity, EntityParameters param) {
var changedFields = new ArrayList<FightProperty>(); var changedFields = new ArrayList<FightProperty>();
if (param.maxHP != -1) { if (param.maxHP != -1) {
setFightProperty(entity, FightProperty.FIGHT_PROP_MAX_HP, param.maxHP, changedFields); setFightProperty(entity, FightProperty.FIGHT_PROP_MAX_HP, param.maxHP, changedFields);
} }
if (param.hp != -1) { if (param.hp != -1) {
float targetHp = param.hp == 0 ? Float.MAX_VALUE : param.hp; float targetHp = param.hp == 0 ? Float.MAX_VALUE : param.hp;
float oldHp = entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP); float oldHp = entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
setFightProperty(entity, FightProperty.FIGHT_PROP_CUR_HP, targetHp, changedFields); setFightProperty(entity, FightProperty.FIGHT_PROP_CUR_HP, targetHp, changedFields);
EntityDamageEvent event = EntityDamageEvent event =
new EntityDamageEvent(entity, oldHp - targetHp, ElementType.None, null); new EntityDamageEvent(entity, oldHp - targetHp, ElementType.None, null);
callHPEvents(entity, event); callHPEvents(entity, event);
} }
if (param.atk != -1) { if (param.atk != -1) {
setFightProperty(entity, FightProperty.FIGHT_PROP_ATTACK, param.atk, changedFields); setFightProperty(entity, FightProperty.FIGHT_PROP_ATTACK, param.atk, changedFields);
setFightProperty(entity, FightProperty.FIGHT_PROP_CUR_ATTACK, param.atk, changedFields); setFightProperty(entity, FightProperty.FIGHT_PROP_CUR_ATTACK, param.atk, changedFields);
} }
if (param.def != -1) { if (param.def != -1) {
setFightProperty(entity, FightProperty.FIGHT_PROP_DEFENSE, param.def, changedFields); setFightProperty(entity, FightProperty.FIGHT_PROP_DEFENSE, param.def, changedFields);
setFightProperty(entity, FightProperty.FIGHT_PROP_CUR_DEFENSE, param.def, changedFields); setFightProperty(entity, FightProperty.FIGHT_PROP_CUR_DEFENSE, param.def, changedFields);
} }
if (!changedFields.isEmpty()) { if (!changedFields.isEmpty()) {
entity entity
.getScene() .getScene()
.broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, changedFields)); .broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, changedFields));
} }
} }
private void callHPEvents(GameEntity entity, EntityDamageEvent event) { private void callHPEvents(GameEntity entity, EntityDamageEvent event) {
entity.runLuaCallbacks(event); entity.runLuaCallbacks(event);
} }
private void setFightProperty( private void setFightProperty(
GameEntity entity, FightProperty property, float value, List<FightProperty> modifiedProps) { GameEntity entity, FightProperty property, float value, List<FightProperty> modifiedProps) {
entity.setFightProperty(property, value); entity.setFightProperty(property, value);
modifiedProps.add(property); modifiedProps.add(property);
} }
private static class EntityParameters { private static class EntityParameters {
@Setter public int configId = -1; @Setter public int configId = -1;
@Setter public int state = -1; @Setter public int state = -1;
@Setter public int hp = -1; @Setter public int hp = -1;
@Setter public int maxHP = -1; @Setter public int maxHP = -1;
@Setter public int atk = -1; @Setter public int atk = -1;
@Setter public int def = -1; @Setter public int def = -1;
@Setter public int ai = -1; @Setter public int ai = -1;
public Scene scene = null; public Scene scene = null;
} }
} }

View File

@ -1,279 +1,279 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.tower.TowerLevelRecord; import emu.grasscutter.game.tower.TowerLevelRecord;
import emu.grasscutter.server.packet.send.PacketOpenStateChangeNotify; import emu.grasscutter.server.packet.send.PacketOpenStateChangeNotify;
import emu.grasscutter.server.packet.send.PacketSceneAreaUnlockNotify; import emu.grasscutter.server.packet.send.PacketSceneAreaUnlockNotify;
import emu.grasscutter.server.packet.send.PacketScenePointUnlockNotify; import emu.grasscutter.server.packet.send.PacketScenePointUnlockNotify;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@Command( @Command(
label = "setProp", label = "setProp",
aliases = {"prop"}, aliases = {"prop"},
usage = {"<prop> <value>"}, usage = {"<prop> <value>"},
permission = "player.setprop", permission = "player.setprop",
permissionTargeted = "player.setprop.others") permissionTargeted = "player.setprop.others")
public final class SetPropCommand implements CommandHandler { public final class SetPropCommand implements CommandHandler {
// List of map areas. Unfortunately, there is no readily available source for them in excels or // List of map areas. Unfortunately, there is no readily available source for them in excels or
// bins. // bins.
private static final List<Integer> sceneAreas = private static final List<Integer> sceneAreas =
List.of( List.of(
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 32, 100, 101, 102, 103, 200, 210, 300, 400, 401, 402, 403); 28, 29, 32, 100, 101, 102, 103, 200, 210, 300, 400, 401, 402, 403);
Map<String, Prop> props; Map<String, Prop> props;
public SetPropCommand() { public SetPropCommand() {
this.props = new HashMap<>(); this.props = new HashMap<>();
// Full PlayerProperty enum that won't be advertised but can be used by devs // Full PlayerProperty enum that won't be advertised but can be used by devs
for (PlayerProperty prop : PlayerProperty.values()) { for (PlayerProperty prop : PlayerProperty.values()) {
String name = prop.toString().substring(5); // PROP_EXP -> EXP String name = prop.toString().substring(5); // PROP_EXP -> EXP
String key = name.toLowerCase(); // EXP -> exp String key = name.toLowerCase(); // EXP -> exp
this.props.put(key, new Prop(name, prop)); this.props.put(key, new Prop(name, prop));
} }
// Add special props // Add special props
Prop worldlevel = Prop worldlevel =
new Prop("World Level", PlayerProperty.PROP_PLAYER_WORLD_LEVEL, PseudoProp.WORLD_LEVEL); new Prop("World Level", PlayerProperty.PROP_PLAYER_WORLD_LEVEL, PseudoProp.WORLD_LEVEL);
this.props.put("worldlevel", worldlevel); this.props.put("worldlevel", worldlevel);
this.props.put("wl", worldlevel); this.props.put("wl", worldlevel);
Prop abyss = new Prop("Tower Level", PseudoProp.TOWER_LEVEL); Prop abyss = new Prop("Tower Level", PseudoProp.TOWER_LEVEL);
this.props.put("abyss", abyss); this.props.put("abyss", abyss);
this.props.put("abyssfloor", abyss); this.props.put("abyssfloor", abyss);
this.props.put("ut", abyss); this.props.put("ut", abyss);
this.props.put("tower", abyss); this.props.put("tower", abyss);
this.props.put("towerlevel", abyss); this.props.put("towerlevel", abyss);
this.props.put("unlocktower", abyss); this.props.put("unlocktower", abyss);
Prop bplevel = new Prop("BP Level", PseudoProp.BP_LEVEL); Prop bplevel = new Prop("BP Level", PseudoProp.BP_LEVEL);
this.props.put("bplevel", bplevel); this.props.put("bplevel", bplevel);
this.props.put("bp", bplevel); this.props.put("bp", bplevel);
this.props.put("battlepass", bplevel); this.props.put("battlepass", bplevel);
Prop godmode = new Prop("GodMode", PseudoProp.GOD_MODE); Prop godmode = new Prop("GodMode", PseudoProp.GOD_MODE);
this.props.put("godmode", godmode); this.props.put("godmode", godmode);
this.props.put("god", godmode); this.props.put("god", godmode);
Prop nostamina = new Prop("UnlimitedStamina", PseudoProp.UNLIMITED_STAMINA); Prop nostamina = new Prop("UnlimitedStamina", PseudoProp.UNLIMITED_STAMINA);
this.props.put("unlimitedstamina", nostamina); this.props.put("unlimitedstamina", nostamina);
this.props.put("us", nostamina); this.props.put("us", nostamina);
this.props.put("nostamina", nostamina); this.props.put("nostamina", nostamina);
this.props.put("nostam", nostamina); this.props.put("nostam", nostamina);
this.props.put("ns", nostamina); this.props.put("ns", nostamina);
Prop unlimitedenergy = new Prop("UnlimitedEnergy", PseudoProp.UNLIMITED_ENERGY); Prop unlimitedenergy = new Prop("UnlimitedEnergy", PseudoProp.UNLIMITED_ENERGY);
this.props.put("unlimitedenergy", unlimitedenergy); this.props.put("unlimitedenergy", unlimitedenergy);
this.props.put("ue", unlimitedenergy); this.props.put("ue", unlimitedenergy);
Prop setopenstate = new Prop("SetOpenstate", PseudoProp.SET_OPENSTATE); Prop setopenstate = new Prop("SetOpenstate", PseudoProp.SET_OPENSTATE);
this.props.put("setopenstate", setopenstate); this.props.put("setopenstate", setopenstate);
this.props.put("so", setopenstate); this.props.put("so", setopenstate);
Prop unsetopenstate = new Prop("UnsetOpenstate", PseudoProp.UNSET_OPENSTATE); Prop unsetopenstate = new Prop("UnsetOpenstate", PseudoProp.UNSET_OPENSTATE);
this.props.put("unsetopenstate", unsetopenstate); this.props.put("unsetopenstate", unsetopenstate);
this.props.put("uo", unsetopenstate); this.props.put("uo", unsetopenstate);
Prop unlockmap = new Prop("UnlockMap", PseudoProp.UNLOCK_MAP); Prop unlockmap = new Prop("UnlockMap", PseudoProp.UNLOCK_MAP);
this.props.put("unlockmap", unlockmap); this.props.put("unlockmap", unlockmap);
this.props.put("um", unlockmap); this.props.put("um", unlockmap);
} }
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() != 2) { if (args.size() != 2) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
String propStr = args.get(0).toLowerCase(); String propStr = args.get(0).toLowerCase();
String valueStr = args.get(1).toLowerCase(); String valueStr = args.get(1).toLowerCase();
int value; int value;
if (!props.containsKey(propStr)) { if (!props.containsKey(propStr)) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
try { try {
value = value =
switch (valueStr.toLowerCase()) { switch (valueStr.toLowerCase()) {
case "on", "true" -> 1; case "on", "true" -> 1;
case "off", "false" -> 0; case "off", "false" -> 0;
case "toggle" -> -1; case "toggle" -> -1;
default -> Integer.parseInt(valueStr); default -> Integer.parseInt(valueStr);
}; };
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.execution.argument_error"); CommandHandler.sendTranslatedMessage(sender, "commands.execution.argument_error");
return; return;
} }
boolean success = false; boolean success = false;
Prop prop = props.get(propStr); Prop prop = props.get(propStr);
success = success =
switch (prop.pseudoProp) { switch (prop.pseudoProp) {
case WORLD_LEVEL -> targetPlayer.setWorldLevel(value); case WORLD_LEVEL -> targetPlayer.setWorldLevel(value);
case BP_LEVEL -> targetPlayer.getBattlePassManager().setLevel(value); case BP_LEVEL -> targetPlayer.getBattlePassManager().setLevel(value);
case TOWER_LEVEL -> this.setTowerLevel(sender, targetPlayer, value); case TOWER_LEVEL -> this.setTowerLevel(sender, targetPlayer, value);
case GOD_MODE, UNLIMITED_STAMINA, UNLIMITED_ENERGY -> this.setBool( case GOD_MODE, UNLIMITED_STAMINA, UNLIMITED_ENERGY -> this.setBool(
sender, targetPlayer, prop.pseudoProp, value); sender, targetPlayer, prop.pseudoProp, value);
case SET_OPENSTATE -> this.setOpenState(targetPlayer, value, 1); case SET_OPENSTATE -> this.setOpenState(targetPlayer, value, 1);
case UNSET_OPENSTATE -> this.setOpenState(targetPlayer, value, 0); case UNSET_OPENSTATE -> this.setOpenState(targetPlayer, value, 0);
case UNLOCK_MAP -> unlockMap(targetPlayer); case UNLOCK_MAP -> unlockMap(targetPlayer);
default -> targetPlayer.setProperty(prop.prop, value); default -> targetPlayer.setProperty(prop.prop, value);
}; };
if (success) { if (success) {
if (targetPlayer == sender) { if (targetPlayer == sender) {
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
sender, "commands.generic.set_to", prop.name, valueStr); sender, "commands.generic.set_to", prop.name, valueStr);
} else { } else {
String uidStr = targetPlayer.getAccount().getId(); String uidStr = targetPlayer.getAccount().getId();
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
sender, "commands.generic.set_for_to", prop.name, uidStr, valueStr); sender, "commands.generic.set_for_to", prop.name, uidStr, valueStr);
} }
} else { } else {
if (prop.prop if (prop.prop
!= PlayerProperty.PROP_NONE) { // PseudoProps need to do their own error messages != PlayerProperty.PROP_NONE) { // PseudoProps need to do their own error messages
int min = targetPlayer.getPropertyMin(prop.prop); int min = targetPlayer.getPropertyMin(prop.prop);
int max = targetPlayer.getPropertyMax(prop.prop); int max = targetPlayer.getPropertyMax(prop.prop);
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
sender, "commands.generic.invalid.value_between", prop.name, min, max); sender, "commands.generic.invalid.value_between", prop.name, min, max);
} }
} }
} }
private boolean setTowerLevel(Player sender, Player targetPlayer, int topFloor) { private boolean setTowerLevel(Player sender, Player targetPlayer, int topFloor) {
List<Integer> floorIds = targetPlayer.getServer().getTowerSystem().getAllFloors(); List<Integer> floorIds = targetPlayer.getServer().getTowerSystem().getAllFloors();
if (topFloor < 0 || topFloor > floorIds.size()) { if (topFloor < 0 || topFloor > floorIds.size()) {
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
sender, "commands.generic.invalid.value_between", "Tower Level", 0, floorIds.size()); sender, "commands.generic.invalid.value_between", "Tower Level", 0, floorIds.size());
return false; return false;
} }
Map<Integer, TowerLevelRecord> recordMap = targetPlayer.getTowerManager().getRecordMap(); Map<Integer, TowerLevelRecord> recordMap = targetPlayer.getTowerManager().getRecordMap();
// Add records for each unlocked floor // Add records for each unlocked floor
for (int floor : floorIds.subList(0, topFloor)) { for (int floor : floorIds.subList(0, topFloor)) {
if (!recordMap.containsKey(floor)) { if (!recordMap.containsKey(floor)) {
recordMap.put(floor, new TowerLevelRecord(floor)); recordMap.put(floor, new TowerLevelRecord(floor));
} }
} }
// Remove records for each floor past our target // Remove records for each floor past our target
for (int floor : floorIds.subList(topFloor, floorIds.size())) { for (int floor : floorIds.subList(topFloor, floorIds.size())) {
recordMap.remove(floor); recordMap.remove(floor);
} }
// Six stars required on Floor 8 to unlock Floor 9+ // Six stars required on Floor 8 to unlock Floor 9+
if (topFloor > 8) { if (topFloor > 8) {
recordMap recordMap
.get(floorIds.get(7)) .get(floorIds.get(7))
.setLevelStars( .setLevelStars(
0, 0,
6); // levelIds seem to start at 1 for Floor 1 Chamber 1, so this doesn't get shown at 6); // levelIds seem to start at 1 for Floor 1 Chamber 1, so this doesn't get shown at
// all // all
} }
return true; return true;
} }
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.isInGodMode(); case GOD_MODE -> targetPlayer.isInGodMode();
case UNLIMITED_STAMINA -> targetPlayer.isUnlimitedStamina(); case UNLIMITED_STAMINA -> targetPlayer.isUnlimitedStamina();
case UNLIMITED_ENERGY -> !targetPlayer.getEnergyManager().isEnergyUsage(); case UNLIMITED_ENERGY -> !targetPlayer.getEnergyManager().isEnergyUsage();
default -> false; default -> false;
}; };
enabled = enabled =
switch (value) { switch (value) {
case -1 -> !enabled; case -1 -> !enabled;
case 0 -> false; case 0 -> false;
default -> true; default -> true;
}; };
switch (pseudoProp) { switch (pseudoProp) {
case GOD_MODE: case GOD_MODE:
targetPlayer.setInGodMode(enabled); targetPlayer.setInGodMode(enabled);
break; break;
case UNLIMITED_STAMINA: case UNLIMITED_STAMINA:
targetPlayer.setUnlimitedStamina(enabled); targetPlayer.setUnlimitedStamina(enabled);
break; break;
case UNLIMITED_ENERGY: case UNLIMITED_ENERGY:
targetPlayer.getEnergyManager().setEnergyUsage(!enabled); targetPlayer.getEnergyManager().setEnergyUsage(!enabled);
break; break;
default: default:
return false; return false;
} }
return true; return true;
} }
private boolean setOpenState(Player targetPlayer, int state, int value) { private boolean setOpenState(Player targetPlayer, int state, int value) {
targetPlayer.sendPacket(new PacketOpenStateChangeNotify(state, value)); targetPlayer.sendPacket(new PacketOpenStateChangeNotify(state, value));
return true; return true;
} }
private boolean unlockMap(Player targetPlayer) { private boolean unlockMap(Player targetPlayer) {
// Unlock. // Unlock.
GameData.getScenePointsPerScene() GameData.getScenePointsPerScene()
.forEach( .forEach(
(sceneId, scenePoints) -> { (sceneId, scenePoints) -> {
// Unlock trans points. // Unlock trans points.
targetPlayer.getUnlockedScenePoints(sceneId).addAll(scenePoints); targetPlayer.getUnlockedScenePoints(sceneId).addAll(scenePoints);
// Unlock map areas. // Unlock map areas.
targetPlayer.getUnlockedSceneAreas(sceneId).addAll(sceneAreas); targetPlayer.getUnlockedSceneAreas(sceneId).addAll(sceneAreas);
}); });
// Send notify. // Send notify.
int playerScene = targetPlayer.getSceneId(); int playerScene = targetPlayer.getSceneId();
targetPlayer.sendPacket( targetPlayer.sendPacket(
new PacketScenePointUnlockNotify( new PacketScenePointUnlockNotify(
playerScene, targetPlayer.getUnlockedScenePoints(playerScene))); playerScene, targetPlayer.getUnlockedScenePoints(playerScene)));
targetPlayer.sendPacket( targetPlayer.sendPacket(
new PacketSceneAreaUnlockNotify( new PacketSceneAreaUnlockNotify(
playerScene, targetPlayer.getUnlockedSceneAreas(playerScene))); playerScene, targetPlayer.getUnlockedSceneAreas(playerScene)));
return true; return true;
} }
enum PseudoProp { enum PseudoProp {
NONE, NONE,
WORLD_LEVEL, WORLD_LEVEL,
TOWER_LEVEL, TOWER_LEVEL,
BP_LEVEL, BP_LEVEL,
GOD_MODE, GOD_MODE,
UNLIMITED_STAMINA, UNLIMITED_STAMINA,
UNLIMITED_ENERGY, UNLIMITED_ENERGY,
SET_OPENSTATE, SET_OPENSTATE,
UNSET_OPENSTATE, UNSET_OPENSTATE,
UNLOCK_MAP UNLOCK_MAP
} }
static class Prop { static class Prop {
String name; String name;
PlayerProperty prop; PlayerProperty prop;
PseudoProp pseudoProp; PseudoProp pseudoProp;
public Prop(PlayerProperty prop) { public Prop(PlayerProperty prop) {
this(prop.toString(), prop, PseudoProp.NONE); this(prop.toString(), prop, PseudoProp.NONE);
} }
public Prop(String name) { public Prop(String name) {
this(name, PlayerProperty.PROP_NONE, PseudoProp.NONE); this(name, PlayerProperty.PROP_NONE, PseudoProp.NONE);
} }
public Prop(String name, PseudoProp pseudoProp) { public Prop(String name, PseudoProp pseudoProp) {
this(name, PlayerProperty.PROP_NONE, pseudoProp); this(name, PlayerProperty.PROP_NONE, pseudoProp);
} }
public Prop(String name, PlayerProperty prop) { public Prop(String name, PlayerProperty prop) {
this(name, prop, PseudoProp.NONE); this(name, prop, PseudoProp.NONE);
} }
public Prop(String name, PlayerProperty prop, PseudoProp pseudoProp) { public Prop(String name, PlayerProperty prop, PseudoProp pseudoProp) {
this.name = name; this.name = name;
this.prop = prop; this.prop = prop;
this.pseudoProp = pseudoProp; this.pseudoProp = pseudoProp;
} }
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,58 +1,70 @@
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; import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.Position; import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntList; import lombok.Getter;
import lombok.Getter; import lombok.Setter;
import lombok.Setter;
public final 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 pos; @Getter private Position rot;
@Getter private Position rot; @Getter private Position size;
@Getter private Position size;
@SerializedName(
@SerializedName(value="dungeonIds", alternate={"JHHFPGJNMIN"}) value = "dungeonIds",
@Getter private int[] dungeonIds; alternate = {"JHHFPGJNMIN"})
@Getter
@SerializedName(value="dungeonRandomList", alternate={"OIBKFJNBLHO"}) private int[] dungeonIds;
@Getter private int[] dungeonRandomList;
@SerializedName(
@SerializedName(value="groupIDs", alternate={"HFOBOOHKBGF"}) value = "dungeonRandomList",
@Getter private int[] groupIDs; alternate = {"OIBKFJNBLHO"})
@Getter
@SerializedName(value="tranSceneId", alternate={"JHBICGBAPIH"}) private int[] dungeonRandomList;
@Getter @Setter private int tranSceneId;
@SerializedName(
public String getType() { value = "groupIDs",
return $type; alternate = {"HFOBOOHKBGF"})
} @Getter
private int[] groupIDs;
public void updateDailyDungeon() {
if (this.dungeonRandomList == null || this.dungeonRandomList.length == 0) { @SerializedName(
return; value = "tranSceneId",
} alternate = {"JHBICGBAPIH"})
@Getter
IntList newDungeons = new IntArrayList(); @Setter
int day = Grasscutter.getCurrentDayOfWeek(); private int tranSceneId;
for (int randomId : this.dungeonRandomList) { public String getType() {
DailyDungeonData data = GameData.getDailyDungeonDataMap().get(randomId); return $type;
}
if (data != null) {
for (int d : data.getDungeonsByDay(day)) { public void updateDailyDungeon() {
newDungeons.add(d); if (this.dungeonRandomList == null || this.dungeonRandomList.length == 0) {
} return;
} }
}
IntList newDungeons = new IntArrayList();
this.dungeonIds = newDungeons.toIntArray(); int day = Grasscutter.getCurrentDayOfWeek();
}
} for (int randomId : this.dungeonRandomList) {
DailyDungeonData data = GameData.getDailyDungeonDataMap().get(randomId);
if (data != null) {
for (int d : data.getDungeonsByDay(day)) {
newDungeons.add(d);
}
}
}
this.dungeonIds = newDungeons.toIntArray();
}
}

View File

@ -1,22 +1,22 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.EntityType; import emu.grasscutter.game.props.EntityType;
import lombok.Getter; import lombok.Getter;
@ResourceType(name = "GadgetExcelConfigData.json") @ResourceType(name = "GadgetExcelConfigData.json")
@Getter @Getter
public final class GadgetData extends GameResource { public final class GadgetData extends GameResource {
@Getter(onMethod_ = @Override) @Getter(onMethod_ = @Override)
private int id; private int id;
private EntityType type; private EntityType type;
private String jsonName; private String jsonName;
private boolean isInteractive; private boolean isInteractive;
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; private String visionLevel;
} }

View File

@ -1,120 +1,133 @@
package emu.grasscutter.data.excels.monster; package emu.grasscutter.data.excels.monster;
import java.util.List; import com.google.gson.annotations.SerializedName;
import java.util.Map.Entry; import emu.grasscutter.data.GameData;
import java.util.Set; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import com.google.gson.annotations.SerializedName; import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.common.PropGrowCurve;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.data.GameResource; import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.game.props.MonsterType;
import emu.grasscutter.data.ResourceType.LoadPriority; import java.util.List;
import emu.grasscutter.data.common.PropGrowCurve; import java.util.Map.Entry;
import emu.grasscutter.data.excels.GadgetData; import java.util.Set;
import emu.grasscutter.game.props.FightProperty; import lombok.Getter;
import emu.grasscutter.game.props.MonsterType;
import lombok.Getter; @ResourceType(name = "MonsterExcelConfigData.json", loadPriority = LoadPriority.LOW)
@Getter
@ResourceType(name = "MonsterExcelConfigData.json", loadPriority = LoadPriority.LOW) public class MonsterData extends GameResource {
@Getter public static Set<FightProperty> definedFightProperties =
public class MonsterData extends GameResource { Set.of(
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); FightProperty.FIGHT_PROP_BASE_HP,
FightProperty.FIGHT_PROP_BASE_ATTACK,
@Getter(onMethod_ = @Override) FightProperty.FIGHT_PROP_BASE_DEFENSE,
private int id; FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT,
FightProperty.FIGHT_PROP_FIRE_SUB_HURT,
private String monsterName; FightProperty.FIGHT_PROP_ELEC_SUB_HURT,
private MonsterType type; FightProperty.FIGHT_PROP_WATER_SUB_HURT,
private String serverScript; FightProperty.FIGHT_PROP_GRASS_SUB_HURT,
private List<Integer> affix; FightProperty.FIGHT_PROP_WIND_SUB_HURT,
private String ai; FightProperty.FIGHT_PROP_ROCK_SUB_HURT,
private int[] equips; FightProperty.FIGHT_PROP_ICE_SUB_HURT);
private List<HpDrops> hpDrops;
private int killDropId; @Getter(onMethod_ = @Override)
private String excludeWeathers; private int id;
private int featureTagGroupID;
private int mpPropID; private String monsterName;
private String skin; private MonsterType type;
private int describeId; private String serverScript;
private int combatBGMLevel; private List<Integer> affix;
private int entityBudgetLevel; private String ai;
private int[] equips;
@SerializedName("hpBase") private List<HpDrops> hpDrops;
private float baseHp; private int killDropId;
@SerializedName("attackBase") private String excludeWeathers;
private float baseAttack; private int featureTagGroupID;
@SerializedName("defenseBase") private int mpPropID;
private float baseDefense; private String skin;
private int describeId;
private float fireSubHurt; private int combatBGMLevel;
private float elecSubHurt; private int entityBudgetLevel;
private float grassSubHurt;
private float waterSubHurt; @SerializedName("hpBase")
private float windSubHurt; private float baseHp;
private float rockSubHurt;
private float iceSubHurt; @SerializedName("attackBase")
private float physicalSubHurt; private float baseAttack;
private List<PropGrowCurve> propGrowCurves;
private long nameTextMapHash; @SerializedName("defenseBase")
private int campID; private float baseDefense;
// Transient private float fireSubHurt;
private int weaponId; private float elecSubHurt;
private MonsterDescribeData describeData; private float grassSubHurt;
private float waterSubHurt;
private int specialNameId; // will only be set if describe data is available private float windSubHurt;
private float rockSubHurt;
@Override private float iceSubHurt;
public void onLoad() { private float physicalSubHurt;
for (int id : this.equips) { private List<PropGrowCurve> propGrowCurves;
if (id == 0) { private long nameTextMapHash;
continue; private int campID;
}
// Transient
GadgetData gadget = GameData.getGadgetDataMap().get(id); private int weaponId;
if (gadget == null) { private MonsterDescribeData describeData;
continue;
} private int specialNameId; // will only be set if describe data is available
if (gadget.getItemJsonName().equals("Default_MonsterWeapon")) { @Override
this.weaponId = id; public void onLoad() {
} for (int id : this.equips) {
} if (id == 0) {
continue;
this.describeData = GameData.getMonsterDescribeDataMap().get(this.getDescribeId()); }
if (this.describeData == null){ GadgetData gadget = GameData.getGadgetDataMap().get(id);
return; if (gadget == null) {
} continue;
for(Entry<Integer, MonsterSpecialNameData> entry: GameData.getMonsterSpecialNameDataMap().entrySet()) { }
if (entry.getValue().getSpecialNameLabId() == this.getDescribeData().getSpecialNameLabId()){
this.specialNameId = entry.getKey(); if (gadget.getItemJsonName().equals("Default_MonsterWeapon")) {
break; this.weaponId = id;
} }
} }
}
this.describeData = GameData.getMonsterDescribeDataMap().get(this.getDescribeId());
public float getFightProperty(FightProperty prop) {
return switch (prop) { if (this.describeData == null) {
case FIGHT_PROP_BASE_HP -> this.baseHp; return;
case FIGHT_PROP_BASE_ATTACK -> this.baseAttack; }
case FIGHT_PROP_BASE_DEFENSE -> this.baseDefense; for (Entry<Integer, MonsterSpecialNameData> entry :
case FIGHT_PROP_PHYSICAL_SUB_HURT -> this.physicalSubHurt; GameData.getMonsterSpecialNameDataMap().entrySet()) {
case FIGHT_PROP_FIRE_SUB_HURT -> this.fireSubHurt; if (entry.getValue().getSpecialNameLabId() == this.getDescribeData().getSpecialNameLabId()) {
case FIGHT_PROP_ELEC_SUB_HURT -> this.elecSubHurt; this.specialNameId = entry.getKey();
case FIGHT_PROP_WATER_SUB_HURT -> this.waterSubHurt; break;
case FIGHT_PROP_GRASS_SUB_HURT -> this.grassSubHurt; }
case FIGHT_PROP_WIND_SUB_HURT -> this.windSubHurt; }
case FIGHT_PROP_ROCK_SUB_HURT -> this.rockSubHurt; }
case FIGHT_PROP_ICE_SUB_HURT -> this.iceSubHurt;
default -> 0f; public float getFightProperty(FightProperty prop) {
}; return switch (prop) {
} case FIGHT_PROP_BASE_HP -> this.baseHp;
case FIGHT_PROP_BASE_ATTACK -> this.baseAttack;
@Getter case FIGHT_PROP_BASE_DEFENSE -> this.baseDefense;
public class HpDrops { case FIGHT_PROP_PHYSICAL_SUB_HURT -> this.physicalSubHurt;
private int DropId; case FIGHT_PROP_FIRE_SUB_HURT -> this.fireSubHurt;
private int HpPercent; case FIGHT_PROP_ELEC_SUB_HURT -> this.elecSubHurt;
} case FIGHT_PROP_WATER_SUB_HURT -> this.waterSubHurt;
} case FIGHT_PROP_GRASS_SUB_HURT -> this.grassSubHurt;
case FIGHT_PROP_WIND_SUB_HURT -> this.windSubHurt;
case FIGHT_PROP_ROCK_SUB_HURT -> this.rockSubHurt;
case FIGHT_PROP_ICE_SUB_HURT -> this.iceSubHurt;
default -> 0f;
};
}
@Getter
public class HpDrops {
private int DropId;
private int HpPercent;
}
}

View File

@ -1,21 +1,28 @@
package emu.grasscutter.data.excels.monster; package emu.grasscutter.data.excels.monster;
import com.google.gson.annotations.SerializedName; 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;
import lombok.Getter; import lombok.Getter;
@ResourceType(name = "MonsterDescribeExcelConfigData.json", loadPriority = LoadPriority.HIGH) @ResourceType(name = "MonsterDescribeExcelConfigData.json", loadPriority = LoadPriority.HIGH)
@Getter @Getter
public class MonsterDescribeData extends GameResource { public class MonsterDescribeData extends GameResource {
@Getter(onMethod_ = @Override) @Getter(onMethod_ = @Override)
private int id; private int id;
private long nameTextMapHash; private long nameTextMapHash;
@SerializedName(value = "titleId", alternate={"titleID"})
private int titleId; @SerializedName(
@SerializedName(value = "specialNameLabId", alternate={"specialNameLabID"}) value = "titleId",
private int specialNameLabId; alternate = {"titleID"})
private MonsterSpecialNameData specialNameData; private int titleId;
}
@SerializedName(
value = "specialNameLabId",
alternate = {"specialNameLabID"})
private int specialNameLabId;
private MonsterSpecialNameData specialNameData;
}

View File

@ -1,464 +1,464 @@
package emu.grasscutter.database; package emu.grasscutter.database;
import static com.mongodb.client.model.Filters.eq; import static com.mongodb.client.model.Filters.eq;
import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.DeleteResult;
import dev.morphia.query.FindOptions; import dev.morphia.query.FindOptions;
import dev.morphia.query.Sort; import dev.morphia.query.Sort;
import dev.morphia.query.experimental.filters.Filters; import dev.morphia.query.experimental.filters.Filters;
import emu.grasscutter.GameConstants; import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.Account; import emu.grasscutter.game.Account;
import emu.grasscutter.game.achievement.Achievements; import emu.grasscutter.game.achievement.Achievements;
import emu.grasscutter.game.activity.PlayerActivityData; import emu.grasscutter.game.activity.PlayerActivityData;
import emu.grasscutter.game.activity.musicgame.MusicGameBeatmap; import emu.grasscutter.game.activity.musicgame.MusicGameBeatmap;
import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.battlepass.BattlePassManager; import emu.grasscutter.game.battlepass.BattlePassManager;
import emu.grasscutter.game.friends.Friendship; import emu.grasscutter.game.friends.Friendship;
import emu.grasscutter.game.gacha.GachaRecord; import emu.grasscutter.game.gacha.GachaRecord;
import emu.grasscutter.game.home.GameHome; import emu.grasscutter.game.home.GameHome;
import emu.grasscutter.game.inventory.GameItem; 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 emu.grasscutter.game.world.SceneGroupInstance;
import java.util.List;
import java.util.List; import java.util.stream.Stream;
import java.util.stream.Stream;
public final class DatabaseHelper {
public final class DatabaseHelper { public static Account createAccount(String username) {
public static Account createAccount(String username) { return createAccountWithUid(username, 0);
return createAccountWithUid(username, 0); }
}
public static Account createAccountWithUid(String username, int reservedUid) {
public static Account createAccountWithUid(String username, int reservedUid) { // Unique names only
// Unique names only if (DatabaseHelper.checkIfAccountExists(username)) {
if (DatabaseHelper.checkIfAccountExists(username)) { return null;
return null; }
}
// Make sure there are no id collisions
// Make sure there are no id collisions if (reservedUid > 0) {
if (reservedUid > 0) { // Cannot make account with the same uid as the server console
// Cannot make account with the same uid as the server console if (reservedUid == GameConstants.SERVER_CONSOLE_UID) {
if (reservedUid == GameConstants.SERVER_CONSOLE_UID) { return null;
return null; }
}
if (DatabaseHelper.checkIfAccountExists(reservedUid)) {
if (DatabaseHelper.checkIfAccountExists(reservedUid)) { return null;
return null; }
}
// Make sure no existing player already has this id.
// Make sure no existing player already has this id. if (DatabaseHelper.checkIfPlayerExists(reservedUid)) {
if (DatabaseHelper.checkIfPlayerExists(reservedUid)) { return null;
return null; }
} }
}
// Account
// Account Account account = new Account();
Account account = new Account(); account.setUsername(username);
account.setUsername(username); account.setId(Integer.toString(DatabaseManager.getNextId(account)));
account.setId(Integer.toString(DatabaseManager.getNextId(account)));
if (reservedUid > 0) {
if (reservedUid > 0) { account.setReservedPlayerUid(reservedUid);
account.setReservedPlayerUid(reservedUid); }
}
DatabaseHelper.saveAccount(account);
DatabaseHelper.saveAccount(account); return account;
return account; }
}
@Deprecated
@Deprecated public static Account createAccountWithPassword(String username, String password) {
public static Account createAccountWithPassword(String username, String password) { // Unique names only
// Unique names only Account exists = DatabaseHelper.getAccountByName(username);
Account exists = DatabaseHelper.getAccountByName(username); if (exists != null) {
if (exists != null) { return null;
return null; }
}
// Account
// Account Account account = new Account();
Account account = new Account(); account.setId(Integer.toString(DatabaseManager.getNextId(account)));
account.setId(Integer.toString(DatabaseManager.getNextId(account))); account.setUsername(username);
account.setUsername(username); account.setPassword(password);
account.setPassword(password); DatabaseHelper.saveAccount(account);
DatabaseHelper.saveAccount(account); return account;
return account; }
}
public static void saveAccount(Account account) {
public static void saveAccount(Account account) { DatabaseManager.getAccountDatastore().save(account);
DatabaseManager.getAccountDatastore().save(account); }
}
public static Account getAccountByName(String username) {
public static Account getAccountByName(String username) { return DatabaseManager.getAccountDatastore()
return DatabaseManager.getAccountDatastore() .find(Account.class)
.find(Account.class) .filter(Filters.eq("username", username))
.filter(Filters.eq("username", username)) .first();
.first(); }
}
public static Account getAccountByToken(String token) {
public static Account getAccountByToken(String token) { if (token == null) return null;
if (token == null) return null; return DatabaseManager.getAccountDatastore()
return DatabaseManager.getAccountDatastore() .find(Account.class)
.find(Account.class) .filter(Filters.eq("token", token))
.filter(Filters.eq("token", token)) .first();
.first(); }
}
public static Account getAccountBySessionKey(String sessionKey) {
public static Account getAccountBySessionKey(String sessionKey) { if (sessionKey == null) return null;
if (sessionKey == null) return null; return DatabaseManager.getAccountDatastore()
return DatabaseManager.getAccountDatastore() .find(Account.class)
.find(Account.class) .filter(Filters.eq("sessionKey", sessionKey))
.filter(Filters.eq("sessionKey", sessionKey)) .first();
.first(); }
}
public static Account getAccountById(String uid) {
public static Account getAccountById(String uid) { return DatabaseManager.getAccountDatastore()
return DatabaseManager.getAccountDatastore() .find(Account.class)
.find(Account.class) .filter(Filters.eq("_id", uid))
.filter(Filters.eq("_id", uid)) .first();
.first(); }
}
public static Account getAccountByPlayerId(int playerId) {
public static Account getAccountByPlayerId(int playerId) { return DatabaseManager.getAccountDatastore()
return DatabaseManager.getAccountDatastore() .find(Account.class)
.find(Account.class) .filter(Filters.eq("reservedPlayerId", playerId))
.filter(Filters.eq("reservedPlayerId", playerId)) .first();
.first(); }
}
public static boolean checkIfAccountExists(String name) {
public static boolean checkIfAccountExists(String name) { return DatabaseManager.getAccountDatastore()
return DatabaseManager.getAccountDatastore() .find(Account.class)
.find(Account.class) .filter(Filters.eq("username", name))
.filter(Filters.eq("username", name)) .count()
.count() > 0;
> 0; }
}
public static boolean checkIfAccountExists(int reservedUid) {
public static boolean checkIfAccountExists(int reservedUid) { return DatabaseManager.getAccountDatastore()
return DatabaseManager.getAccountDatastore() .find(Account.class)
.find(Account.class) .filter(Filters.eq("reservedPlayerId", reservedUid))
.filter(Filters.eq("reservedPlayerId", reservedUid)) .count()
.count() > 0;
> 0; }
}
public static synchronized void deleteAccount(Account target) {
public static synchronized void deleteAccount(Account target) { // To delete an account, we need to also delete all the other documents in the database that
// To delete an account, we need to also delete all the other documents in the database that // reference the account.
// reference the account. // This should optimally be wrapped inside a transaction, to make sure an error thrown mid-way
// This should optimally be wrapped inside a transaction, to make sure an error thrown mid-way // does not leave the
// does not leave the // database in an inconsistent state, but unfortunately Mongo only supports that when we have a
// database in an inconsistent state, but unfortunately Mongo only supports that when we have a // replica set ...
// replica set ...
Player player = Grasscutter.getGameServer().getPlayerByAccountId(target.getId());
Player player = Grasscutter.getGameServer().getPlayerByAccountId(target.getId());
// Close session first
// Close session first if (player != null) {
if (player != null) { player.getSession().close();
player.getSession().close(); } else {
} else { player = getPlayerByAccount(target);
player = getPlayerByAccount(target); if (player == null) return;
if (player == null) return; }
} int uid = player.getUid();
int uid = player.getUid(); // Delete data from collections
// Delete data from collections DatabaseManager.getGameDatabase().getCollection("achievements").deleteMany(eq("uid", uid));
DatabaseManager.getGameDatabase().getCollection("achievements").deleteMany(eq("uid", uid)); DatabaseManager.getGameDatabase().getCollection("activities").deleteMany(eq("uid", uid));
DatabaseManager.getGameDatabase().getCollection("activities").deleteMany(eq("uid", uid)); DatabaseManager.getGameDatabase().getCollection("homes").deleteMany(eq("ownerUid", uid));
DatabaseManager.getGameDatabase().getCollection("homes").deleteMany(eq("ownerUid", uid)); DatabaseManager.getGameDatabase().getCollection("mail").deleteMany(eq("ownerUid", uid));
DatabaseManager.getGameDatabase().getCollection("mail").deleteMany(eq("ownerUid", uid)); DatabaseManager.getGameDatabase().getCollection("avatars").deleteMany(eq("ownerId", uid));
DatabaseManager.getGameDatabase().getCollection("avatars").deleteMany(eq("ownerId", uid)); DatabaseManager.getGameDatabase().getCollection("gachas").deleteMany(eq("ownerId", uid));
DatabaseManager.getGameDatabase().getCollection("gachas").deleteMany(eq("ownerId", uid)); DatabaseManager.getGameDatabase().getCollection("items").deleteMany(eq("ownerId", uid));
DatabaseManager.getGameDatabase().getCollection("items").deleteMany(eq("ownerId", uid)); DatabaseManager.getGameDatabase().getCollection("quests").deleteMany(eq("ownerUid", uid));
DatabaseManager.getGameDatabase().getCollection("quests").deleteMany(eq("ownerUid", uid)); DatabaseManager.getGameDatabase().getCollection("battlepass").deleteMany(eq("ownerUid", uid));
DatabaseManager.getGameDatabase().getCollection("battlepass").deleteMany(eq("ownerUid", uid));
// Delete friendships.
// Delete friendships. // Here, we need to make sure to not only delete the deleted account's friendships,
// Here, we need to make sure to not only delete the deleted account's friendships, // but also all friendship entries for that account's friends.
// but also all friendship entries for that account's friends. DatabaseManager.getGameDatabase().getCollection("friendships").deleteMany(eq("ownerId", uid));
DatabaseManager.getGameDatabase().getCollection("friendships").deleteMany(eq("ownerId", uid)); DatabaseManager.getGameDatabase().getCollection("friendships").deleteMany(eq("friendId", uid));
DatabaseManager.getGameDatabase().getCollection("friendships").deleteMany(eq("friendId", uid));
// Delete the player last.
// Delete the player last. DatabaseManager.getGameDatastore().find(Player.class).filter(Filters.eq("id", uid)).delete();
DatabaseManager.getGameDatastore().find(Player.class).filter(Filters.eq("id", uid)).delete();
// Finally, delete the account itself.
// Finally, delete the account itself. DatabaseManager.getAccountDatastore()
DatabaseManager.getAccountDatastore() .find(Account.class)
.find(Account.class) .filter(Filters.eq("id", target.getId()))
.filter(Filters.eq("id", target.getId())) .delete();
.delete(); }
}
public static <T> Stream<T> getByGameClass(Class<T> classType) {
public static <T> Stream<T> getByGameClass(Class<T> classType) { return DatabaseManager.getGameDatastore().find(classType).stream();
return DatabaseManager.getGameDatastore().find(classType).stream(); }
}
@Deprecated(forRemoval = true)
@Deprecated(forRemoval = true) public static List<Player> getAllPlayers() {
public static List<Player> getAllPlayers() { return DatabaseManager.getGameDatastore().find(Player.class).stream().toList();
return DatabaseManager.getGameDatastore().find(Player.class).stream().toList(); }
}
public static Player getPlayerByUid(int id) {
public static Player getPlayerByUid(int id) { return DatabaseManager.getGameDatastore()
return DatabaseManager.getGameDatastore() .find(Player.class)
.find(Player.class) .filter(Filters.eq("_id", id))
.filter(Filters.eq("_id", id)) .first();
.first(); }
}
@Deprecated
@Deprecated public static Player getPlayerByAccount(Account account) {
public static Player getPlayerByAccount(Account account) { return DatabaseManager.getGameDatastore()
return DatabaseManager.getGameDatastore() .find(Player.class)
.find(Player.class) .filter(Filters.eq("accountId", account.getId()))
.filter(Filters.eq("accountId", account.getId())) .first();
.first(); }
}
public static Player getPlayerByAccount(Account account, Class<? extends Player> playerClass) {
public static Player getPlayerByAccount(Account account, Class<? extends Player> playerClass) { return DatabaseManager.getGameDatastore()
return DatabaseManager.getGameDatastore() .find(playerClass)
.find(playerClass) .filter(Filters.eq("accountId", account.getId()))
.filter(Filters.eq("accountId", account.getId())) .first();
.first(); }
}
public static boolean checkIfPlayerExists(int uid) {
public static boolean checkIfPlayerExists(int uid) { return DatabaseManager.getGameDatastore()
return DatabaseManager.getGameDatastore() .find(Player.class)
.find(Player.class) .filter(Filters.eq("_id", uid))
.filter(Filters.eq("_id", uid)) .count()
.count() > 0;
> 0; }
}
public static synchronized Player generatePlayerUid(Player character, int reservedId) {
public static synchronized Player generatePlayerUid(Player character, int reservedId) { // Check if reserved id
// Check if reserved id int id;
int id; if (reservedId > 0 && !checkIfPlayerExists(reservedId)) {
if (reservedId > 0 && !checkIfPlayerExists(reservedId)) { id = reservedId;
id = reservedId; character.setUid(id);
character.setUid(id); } else {
} else { do {
do { id = DatabaseManager.getNextId(character);
id = DatabaseManager.getNextId(character); } while (checkIfPlayerExists(id));
} while (checkIfPlayerExists(id)); character.setUid(id);
character.setUid(id); }
} // Save to database
// Save to database DatabaseManager.getGameDatastore().save(character);
DatabaseManager.getGameDatastore().save(character); return character;
return character; }
}
public static synchronized int getNextPlayerId(int reservedId) {
public static synchronized int getNextPlayerId(int reservedId) { // Check if reserved id
// Check if reserved id int id;
int id; if (reservedId > 0 && !checkIfPlayerExists(reservedId)) {
if (reservedId > 0 && !checkIfPlayerExists(reservedId)) { id = reservedId;
id = reservedId; } else {
} else { do {
do { id = DatabaseManager.getNextId(Player.class);
id = DatabaseManager.getNextId(Player.class); } while (checkIfPlayerExists(id));
} while (checkIfPlayerExists(id)); }
} return id;
return id; }
}
public static void savePlayer(Player character) {
public static void savePlayer(Player character) { DatabaseManager.getGameDatastore().save(character);
DatabaseManager.getGameDatastore().save(character); }
}
public static void saveAvatar(Avatar avatar) {
public static void saveAvatar(Avatar avatar) { DatabaseManager.getGameDatastore().save(avatar);
DatabaseManager.getGameDatastore().save(avatar); }
}
public static List<Avatar> getAvatars(Player player) {
public static List<Avatar> getAvatars(Player player) { return DatabaseManager.getGameDatastore()
return DatabaseManager.getGameDatastore() .find(Avatar.class)
.find(Avatar.class) .filter(Filters.eq("ownerId", player.getUid()))
.filter(Filters.eq("ownerId", player.getUid())) .stream()
.stream() .toList();
.toList(); }
}
public static void saveItem(GameItem item) {
public static void saveItem(GameItem item) { DatabaseManager.getGameDatastore().save(item);
DatabaseManager.getGameDatastore().save(item); }
}
public static boolean deleteItem(GameItem item) {
public static boolean deleteItem(GameItem item) { DeleteResult result = DatabaseManager.getGameDatastore().delete(item);
DeleteResult result = DatabaseManager.getGameDatastore().delete(item); return result.wasAcknowledged();
return result.wasAcknowledged(); }
}
public static List<GameItem> getInventoryItems(Player player) {
public static List<GameItem> getInventoryItems(Player player) { return DatabaseManager.getGameDatastore()
return DatabaseManager.getGameDatastore() .find(GameItem.class)
.find(GameItem.class) .filter(Filters.eq("ownerId", player.getUid()))
.filter(Filters.eq("ownerId", player.getUid())) .stream()
.stream() .toList();
.toList(); }
}
public static List<Friendship> getFriends(Player player) {
public static List<Friendship> getFriends(Player player) { return DatabaseManager.getGameDatastore()
return DatabaseManager.getGameDatastore() .find(Friendship.class)
.find(Friendship.class) .filter(Filters.eq("ownerId", player.getUid()))
.filter(Filters.eq("ownerId", player.getUid())) .stream()
.stream() .toList();
.toList(); }
}
public static List<Friendship> getReverseFriends(Player player) {
public static List<Friendship> getReverseFriends(Player player) { return DatabaseManager.getGameDatastore()
return DatabaseManager.getGameDatastore() .find(Friendship.class)
.find(Friendship.class) .filter(Filters.eq("friendId", player.getUid()))
.filter(Filters.eq("friendId", player.getUid())) .stream()
.stream() .toList();
.toList(); }
}
public static void saveFriendship(Friendship friendship) {
public static void saveFriendship(Friendship friendship) { DatabaseManager.getGameDatastore().save(friendship);
DatabaseManager.getGameDatastore().save(friendship); }
}
public static void deleteFriendship(Friendship friendship) {
public static void deleteFriendship(Friendship friendship) { DatabaseManager.getGameDatastore().delete(friendship);
DatabaseManager.getGameDatastore().delete(friendship); }
}
public static Friendship getReverseFriendship(Friendship friendship) {
public static Friendship getReverseFriendship(Friendship friendship) { return DatabaseManager.getGameDatastore()
return DatabaseManager.getGameDatastore() .find(Friendship.class)
.find(Friendship.class) .filter(
.filter( Filters.and(
Filters.and( Filters.eq("ownerId", friendship.getFriendId()),
Filters.eq("ownerId", friendship.getFriendId()), Filters.eq("friendId", friendship.getOwnerId())))
Filters.eq("friendId", friendship.getOwnerId()))) .first();
.first(); }
}
public static List<GachaRecord> getGachaRecords(int ownerId, int page, int gachaType) {
public static List<GachaRecord> getGachaRecords(int ownerId, int page, int gachaType) { return getGachaRecords(ownerId, page, gachaType, 10);
return getGachaRecords(ownerId, page, gachaType, 10); }
}
public static List<GachaRecord> getGachaRecords(
public static List<GachaRecord> getGachaRecords( int ownerId, int page, int gachaType, int pageSize) {
int ownerId, int page, int gachaType, int pageSize) { return DatabaseManager.getGameDatastore()
return DatabaseManager.getGameDatastore() .find(GachaRecord.class)
.find(GachaRecord.class) .filter(Filters.eq("ownerId", ownerId), Filters.eq("gachaType", gachaType))
.filter(Filters.eq("ownerId", ownerId), Filters.eq("gachaType", gachaType)) .iterator(
.iterator( new FindOptions()
new FindOptions() .sort(Sort.descending("transactionDate"))
.sort(Sort.descending("transactionDate")) .skip(pageSize * page)
.skip(pageSize * page) .limit(pageSize))
.limit(pageSize)) .toList();
.toList(); }
}
public static long getGachaRecordsMaxPage(int ownerId, int page, int gachaType) {
public static long getGachaRecordsMaxPage(int ownerId, int page, int gachaType) { return getGachaRecordsMaxPage(ownerId, page, gachaType, 10);
return getGachaRecordsMaxPage(ownerId, page, gachaType, 10); }
}
public static long getGachaRecordsMaxPage(int ownerId, int page, int gachaType, int pageSize) {
public static long getGachaRecordsMaxPage(int ownerId, int page, int gachaType, int pageSize) { long count =
long count = DatabaseManager.getGameDatastore()
DatabaseManager.getGameDatastore() .find(GachaRecord.class)
.find(GachaRecord.class) .filter(Filters.eq("ownerId", ownerId), Filters.eq("gachaType", gachaType))
.filter(Filters.eq("ownerId", ownerId), Filters.eq("gachaType", gachaType)) .count();
.count(); return count / 10 + (count % 10 > 0 ? 1 : 0);
return count / 10 + (count % 10 > 0 ? 1 : 0); }
}
public static void saveGachaRecord(GachaRecord gachaRecord) {
public static void saveGachaRecord(GachaRecord gachaRecord) { DatabaseManager.getGameDatastore().save(gachaRecord);
DatabaseManager.getGameDatastore().save(gachaRecord); }
}
public static List<Mail> getAllMail(Player player) {
public static List<Mail> getAllMail(Player player) { return DatabaseManager.getGameDatastore()
return DatabaseManager.getGameDatastore() .find(Mail.class)
.find(Mail.class) .filter(Filters.eq("ownerUid", player.getUid()))
.filter(Filters.eq("ownerUid", player.getUid())) .stream()
.stream() .toList();
.toList(); }
}
public static void saveMail(Mail mail) {
public static void saveMail(Mail mail) { DatabaseManager.getGameDatastore().save(mail);
DatabaseManager.getGameDatastore().save(mail); }
}
public static boolean deleteMail(Mail mail) {
public static boolean deleteMail(Mail mail) { DeleteResult result = DatabaseManager.getGameDatastore().delete(mail);
DeleteResult result = DatabaseManager.getGameDatastore().delete(mail); return result.wasAcknowledged();
return result.wasAcknowledged(); }
}
public static List<GameMainQuest> getAllQuests(Player player) {
public static List<GameMainQuest> getAllQuests(Player player) { return DatabaseManager.getGameDatastore()
return DatabaseManager.getGameDatastore() .find(GameMainQuest.class)
.find(GameMainQuest.class) .filter(Filters.eq("ownerUid", player.getUid()))
.filter(Filters.eq("ownerUid", player.getUid())) .stream()
.stream() .toList();
.toList(); }
}
public static void saveQuest(GameMainQuest quest) {
public static void saveQuest(GameMainQuest quest) { DatabaseManager.getGameDatastore().save(quest);
DatabaseManager.getGameDatastore().save(quest); }
}
public static boolean deleteQuest(GameMainQuest quest) {
public static boolean deleteQuest(GameMainQuest quest) { return DatabaseManager.getGameDatastore().delete(quest).wasAcknowledged();
return DatabaseManager.getGameDatastore().delete(quest).wasAcknowledged(); }
}
public static GameHome getHomeByUid(int id) {
public static GameHome getHomeByUid(int id) { return DatabaseManager.getGameDatastore()
return DatabaseManager.getGameDatastore() .find(GameHome.class)
.find(GameHome.class) .filter(Filters.eq("ownerUid", id))
.filter(Filters.eq("ownerUid", id)) .first();
.first(); }
}
public static void saveHome(GameHome gameHome) {
public static void saveHome(GameHome gameHome) { DatabaseManager.getGameDatastore().save(gameHome);
DatabaseManager.getGameDatastore().save(gameHome); }
}
public static BattlePassManager loadBattlePass(Player player) {
public static BattlePassManager loadBattlePass(Player player) { BattlePassManager manager =
BattlePassManager manager = DatabaseManager.getGameDatastore()
DatabaseManager.getGameDatastore() .find(BattlePassManager.class)
.find(BattlePassManager.class) .filter(Filters.eq("ownerUid", player.getUid()))
.filter(Filters.eq("ownerUid", player.getUid())) .first();
.first(); if (manager == null) {
if (manager == null) { manager = new BattlePassManager(player);
manager = new BattlePassManager(player); manager.save();
manager.save(); } else {
} else { manager.setPlayer(player);
manager.setPlayer(player); }
} return manager;
return manager; }
}
public static void saveBattlePass(BattlePassManager manager) {
public static void saveBattlePass(BattlePassManager manager) { DatabaseManager.getGameDatastore().save(manager);
DatabaseManager.getGameDatastore().save(manager); }
}
public static PlayerActivityData getPlayerActivityData(int uid, int activityId) {
public static PlayerActivityData getPlayerActivityData(int uid, int activityId) { return DatabaseManager.getGameDatastore()
return DatabaseManager.getGameDatastore() .find(PlayerActivityData.class)
.find(PlayerActivityData.class) .filter(Filters.and(Filters.eq("uid", uid), Filters.eq("activityId", activityId)))
.filter(Filters.and(Filters.eq("uid", uid), Filters.eq("activityId", activityId))) .first();
.first(); }
}
public static void savePlayerActivityData(PlayerActivityData playerActivityData) {
public static void savePlayerActivityData(PlayerActivityData playerActivityData) { DatabaseManager.getGameDatastore().save(playerActivityData);
DatabaseManager.getGameDatastore().save(playerActivityData); }
}
public static MusicGameBeatmap getMusicGameBeatmap(long musicShareId) {
public static MusicGameBeatmap getMusicGameBeatmap(long musicShareId) { return DatabaseManager.getGameDatastore()
return DatabaseManager.getGameDatastore() .find(MusicGameBeatmap.class)
.find(MusicGameBeatmap.class) .filter(Filters.eq("musicShareId", musicShareId))
.filter(Filters.eq("musicShareId", musicShareId)) .first();
.first(); }
}
public static void saveMusicGameBeatmap(MusicGameBeatmap musicGameBeatmap) {
public static void saveMusicGameBeatmap(MusicGameBeatmap musicGameBeatmap) { DatabaseManager.getGameDatastore().save(musicGameBeatmap);
DatabaseManager.getGameDatastore().save(musicGameBeatmap); }
}
public static Achievements getAchievementData(int uid) {
public static Achievements getAchievementData(int uid) { return DatabaseManager.getGameDatastore()
return DatabaseManager.getGameDatastore() .find(Achievements.class)
.find(Achievements.class) .filter(Filters.and(Filters.eq("uid", uid)))
.filter(Filters.and(Filters.eq("uid", uid))) .first();
.first(); }
}
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) {
public static void saveGroupInstance(SceneGroupInstance instance) { DatabaseManager.getGameDatastore().save(instance);
DatabaseManager.getGameDatastore().save(instance); }
}
public static SceneGroupInstance loadGroupInstance(int groupId, Player owner) {
public static SceneGroupInstance loadGroupInstance(int groupId, Player owner) { return DatabaseManager.getGameDatastore()
return DatabaseManager.getGameDatastore().find(SceneGroupInstance.class) .find(SceneGroupInstance.class)
.filter(Filters.and(Filters.eq("ownerUid", owner.getUid()), .filter(Filters.and(Filters.eq("ownerUid", owner.getUid()), Filters.eq("groupId", groupId)))
Filters.eq("groupId", groupId))).first(); .first();
} }
} }

View File

@ -1,134 +1,134 @@
package emu.grasscutter.game.activity; package emu.grasscutter.game.activity;
import dev.morphia.annotations.Entity; import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id; import dev.morphia.annotations.Id;
import dev.morphia.annotations.Transient; import dev.morphia.annotations.Transient;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.activity.ActivityWatcherData; import emu.grasscutter.data.excels.activity.ActivityWatcherData;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.net.proto.ActivityWatcherInfoOuterClass; import emu.grasscutter.net.proto.ActivityWatcherInfoOuterClass;
import emu.grasscutter.server.packet.send.PacketActivityUpdateWatcherNotify; import emu.grasscutter.server.packet.send.PacketActivityUpdateWatcherNotify;
import emu.grasscutter.utils.JsonUtils; import emu.grasscutter.utils.JsonUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
@Entity("activities") @Entity("activities")
@Data @Data
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
@Builder(builderMethodName = "of") @Builder(builderMethodName = "of")
public class PlayerActivityData { public class PlayerActivityData {
@Id String id; @Id String id;
int uid; int uid;
int activityId; int activityId;
Map<Integer, WatcherInfo> watcherInfoMap; Map<Integer, WatcherInfo> watcherInfoMap;
/** the detail data of each type of activity (Json format) */ /** the detail data of each type of activity (Json format) */
String detail; String detail;
@Transient Player player; @Transient Player player;
@Transient ActivityHandler activityHandler; @Transient ActivityHandler activityHandler;
public static PlayerActivityData getByPlayer(Player player, int activityId) { public static PlayerActivityData getByPlayer(Player player, int activityId) {
return DatabaseHelper.getPlayerActivityData(player.getUid(), activityId); return DatabaseHelper.getPlayerActivityData(player.getUid(), activityId);
} }
public void save() { public void save() {
DatabaseHelper.savePlayerActivityData(this); DatabaseHelper.savePlayerActivityData(this);
} }
public synchronized void addWatcherProgress(int watcherId) { public synchronized void addWatcherProgress(int watcherId) {
var watcherInfo = watcherInfoMap.get(watcherId); var watcherInfo = watcherInfoMap.get(watcherId);
if (watcherInfo == null) { if (watcherInfo == null) {
return; return;
} }
if (watcherInfo.curProgress >= watcherInfo.totalProgress) { if (watcherInfo.curProgress >= watcherInfo.totalProgress) {
return; return;
} }
watcherInfo.curProgress++; watcherInfo.curProgress++;
getPlayer().sendPacket(new PacketActivityUpdateWatcherNotify(activityId, watcherInfo)); getPlayer().sendPacket(new PacketActivityUpdateWatcherNotify(activityId, watcherInfo));
} }
public List<ActivityWatcherInfoOuterClass.ActivityWatcherInfo> getAllWatcherInfoList() { public List<ActivityWatcherInfoOuterClass.ActivityWatcherInfo> getAllWatcherInfoList() {
return watcherInfoMap.values().stream().map(WatcherInfo::toProto).toList(); return watcherInfoMap.values().stream().map(WatcherInfo::toProto).toList();
} }
public void setDetail(Object detail) { public void setDetail(Object detail) {
this.detail = JsonUtils.encode(detail); this.detail = JsonUtils.encode(detail);
} }
public void takeWatcherReward(int watcherId) { public void takeWatcherReward(int watcherId) {
var watcher = watcherInfoMap.get(watcherId); var watcher = watcherInfoMap.get(watcherId);
if (watcher == null || watcher.isTakenReward()) { if (watcher == null || watcher.isTakenReward()) {
return; return;
} }
var reward = var reward =
Optional.of(watcher) Optional.of(watcher)
.map(WatcherInfo::getMetadata) .map(WatcherInfo::getMetadata)
.map(ActivityWatcherData::getRewardID) .map(ActivityWatcherData::getRewardID)
.map(id -> GameData.getRewardDataMap().get(id.intValue())); .map(id -> GameData.getRewardDataMap().get(id.intValue()));
if (reward.isEmpty()) { if (reward.isEmpty()) {
return; return;
} }
List<GameItem> rewards = new ArrayList<>(); List<GameItem> rewards = new ArrayList<>();
for (ItemParamData param : reward.get().getRewardItemList()) { for (ItemParamData param : reward.get().getRewardItemList()) {
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1))); rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
} }
player.getInventory().addItems(rewards, ActionReason.ActivityWatcher); player.getInventory().addItems(rewards, ActionReason.ActivityWatcher);
watcher.setTakenReward(true); watcher.setTakenReward(true);
save(); save();
} }
@Entity @Entity
@Data @Data
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
@Builder(builderMethodName = "of") @Builder(builderMethodName = "of")
public static class WatcherInfo { public static class WatcherInfo {
int watcherId; int watcherId;
int totalProgress; int totalProgress;
int curProgress; int curProgress;
boolean isTakenReward; boolean isTakenReward;
/** /**
* @return True when the progress of this watcher has reached the total progress. * @return True when the progress of this watcher has reached the total progress.
*/ */
public boolean isFinished(){ public boolean isFinished() {
return this.curProgress >= this.totalProgress; 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())
.totalProgress(watcher.getActivityWatcherData().getProgress()) .totalProgress(watcher.getActivityWatcherData().getProgress())
.isTakenReward(false) .isTakenReward(false)
.build(); .build();
} }
public ActivityWatcherData getMetadata() { public ActivityWatcherData getMetadata() {
return GameData.getActivityWatcherDataMap().get(watcherId); return GameData.getActivityWatcherDataMap().get(watcherId);
} }
public ActivityWatcherInfoOuterClass.ActivityWatcherInfo toProto() { public ActivityWatcherInfoOuterClass.ActivityWatcherInfo toProto() {
return ActivityWatcherInfoOuterClass.ActivityWatcherInfo.newBuilder() return ActivityWatcherInfoOuterClass.ActivityWatcherInfo.newBuilder()
.setWatcherId(watcherId) .setWatcherId(watcherId)
.setCurProgress(curProgress) .setCurProgress(curProgress)
.setTotalProgress(totalProgress) .setTotalProgress(totalProgress)
.setIsTakenReward(isTakenReward) .setIsTakenReward(isTakenReward)
.build(); .build();
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,314 +1,331 @@
package emu.grasscutter.game.dungeons; package emu.grasscutter.game.dungeons;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.dungeon.DungeonData; import emu.grasscutter.data.excels.dungeon.DungeonData;
import emu.grasscutter.data.excels.dungeon.DungeonPassConfigData; import emu.grasscutter.data.excels.dungeon.DungeonPassConfigData;
import emu.grasscutter.game.activity.trialavatar.TrialAvatarActivityHandler; import emu.grasscutter.game.activity.trialavatar.TrialAvatarActivityHandler;
import emu.grasscutter.game.dungeons.dungeon_results.BaseDungeonResult; import emu.grasscutter.game.dungeons.dungeon_results.BaseDungeonResult;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType; import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.ActivityType; import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.game.props.WatcherTriggerType; import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.quest.enums.LogicType; import emu.grasscutter.game.quest.enums.LogicType;
import emu.grasscutter.game.quest.enums.QuestContent; import emu.grasscutter.game.quest.enums.QuestContent;
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.ScriptArgs; import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketDungeonWayPointNotify; import emu.grasscutter.server.packet.send.PacketDungeonWayPointNotify;
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify; import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
import emu.grasscutter.utils.Position; 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 lombok.Getter; import java.util.*;
import lombok.NonNull; import java.util.stream.Collectors;
import lombok.val; import java.util.stream.IntStream;
import javax.annotation.Nullable;
import javax.annotation.Nullable; import lombok.Getter;
import java.util.*; import lombok.NonNull;
import java.util.stream.Collectors; import lombok.val;
import java.util.stream.IntStream;
/**
/** * TODO handle time limits TODO handle respawn points TODO handle team wipes and respawns TODO check
* TODO handle time limits * monster level and levelConfigMap
* TODO handle respawn points */
* TODO handle team wipes and respawns public final class DungeonManager {
* TODO check monster level and levelConfigMap @Getter private final Scene scene;
*/ @Getter private final DungeonData dungeonData;
public final class DungeonManager { @Getter private final DungeonPassConfigData passConfigData;
@Getter private final Scene scene;
@Getter private final DungeonData dungeonData; @Getter private final int[] finishedConditions;
@Getter private final DungeonPassConfigData passConfigData; private final IntSet rewardedPlayers = new IntOpenHashSet();
private final Set<Integer> activeDungeonWayPoints = new HashSet<>();
@Getter private final int[] finishedConditions; private boolean ended = false;
private final IntSet rewardedPlayers = new IntOpenHashSet(); private int newestWayPoint = 0;
private final Set<Integer> activeDungeonWayPoints = new HashSet<>(); @Getter private int startSceneTime = 0;
private boolean ended = false;
private int newestWayPoint = 0; DungeonTrialTeam trialTeam = null;
@Getter private int startSceneTime = 0;
public DungeonManager(@NonNull Scene scene, @NonNull DungeonData dungeonData) {
DungeonTrialTeam trialTeam = null; this.scene = scene;
this.dungeonData = dungeonData;
public DungeonManager(@NonNull Scene scene, @NonNull DungeonData dungeonData) { this.passConfigData = GameData.getDungeonPassConfigDataMap().get(dungeonData.getPassCond());
this.scene = scene; this.finishedConditions = new int[passConfigData.getConds().size()];
this.dungeonData = dungeonData; this.scene.setDungeonManager(this);
this.passConfigData = GameData.getDungeonPassConfigDataMap().get(dungeonData.getPassCond()); }
this.finishedConditions = new int[passConfigData.getConds().size()];
this.scene.setDungeonManager(this); public void triggerEvent(DungeonPassConditionType conditionType, int... params) {
} if (ended) {
return;
public void triggerEvent(DungeonPassConditionType conditionType, int... params) { }
if (ended) { for (int i = 0; i < passConfigData.getConds().size(); i++) {
return; var cond = passConfigData.getConds().get(i);
} if (conditionType == cond.getCondType()) {
for (int i = 0; i < passConfigData.getConds().size(); i++) { if (getScene().getWorld().getServer().getDungeonSystem().triggerCondition(cond, params)) {
var cond = passConfigData.getConds().get(i); finishedConditions[i] = 1;
if (conditionType == cond.getCondType()) { }
if (getScene().getWorld().getServer().getDungeonSystem().triggerCondition(cond, params)) { }
finishedConditions[i] = 1; }
}
if (isFinishedSuccessfully()) {
} finishDungeon();
} }
}
if (isFinishedSuccessfully()) {
finishDungeon(); public boolean isFinishedSuccessfully() {
} return LogicType.calculate(passConfigData.getLogicType(), finishedConditions);
}
}
public int getLevelForMonster(int id) {
public boolean isFinishedSuccessfully() { // TODO should use levelConfigMap? and how?
return LogicType.calculate(passConfigData.getLogicType(), finishedConditions); return dungeonData.getShowLevel();
} }
public int getLevelForMonster(int id) { public boolean activateRespawnPoint(int pointId) {
//TODO should use levelConfigMap? and how? val respawnPoint = GameData.getScenePointEntryById(scene.getId(), pointId);
return dungeonData.getShowLevel();
} if (respawnPoint == null) {
Grasscutter.getLogger().warn("trying to activate unknown respawn point {}", pointId);
public boolean activateRespawnPoint(int pointId) { return false;
val respawnPoint = GameData.getScenePointEntryById(scene.getId(), pointId); }
if (respawnPoint == null) { scene.broadcastPacket(
Grasscutter.getLogger().warn("trying to activate unknown respawn point {}", pointId); new PacketDungeonWayPointNotify(
return false; activeDungeonWayPoints.add(pointId), activeDungeonWayPoints));
} newestWayPoint = pointId;
scene.broadcastPacket(new PacketDungeonWayPointNotify(activeDungeonWayPoints.add(pointId), activeDungeonWayPoints)); Grasscutter.getLogger().debug("[unimplemented respawn] activated respawn point {}", pointId);
newestWayPoint = pointId; return true;
}
Grasscutter.getLogger().debug("[unimplemented respawn] activated respawn point {}", pointId);
return true; @Nullable public Position getRespawnLocation() {
} if (newestWayPoint == 0) { // validity is checked before setting it, so if != 0 its always valid
return null;
@Nullable }
public Position getRespawnLocation() { var pointData = GameData.getScenePointEntryById(scene.getId(), newestWayPoint).getPointData();
if (newestWayPoint == 0) { // validity is checked before setting it, so if != 0 its always valid return pointData.getTranPos() != null ? pointData.getTranPos() : pointData.getPos();
return null; }
}
var pointData = GameData.getScenePointEntryById(scene.getId(), newestWayPoint).getPointData(); public Position getRespawnRotation() {
return pointData.getTranPos() != null ? pointData.getTranPos() : pointData.getPos(); if (newestWayPoint == 0) { // validity is checked before setting it, so if != 0 its always valid
} return null;
}
public Position getRespawnRotation() { val pointData = GameData.getScenePointEntryById(scene.getId(), newestWayPoint).getPointData();
if (newestWayPoint == 0) { // validity is checked before setting it, so if != 0 its always valid return pointData.getRot() != null ? pointData.getRot() : null;
return null; }
}
val pointData = GameData.getScenePointEntryById(scene.getId(), newestWayPoint).getPointData(); public boolean getStatueDrops(Player player, boolean useCondensed, int groupId) {
return pointData.getRot() != null ? pointData.getRot() : null; if (!isFinishedSuccessfully()
} || dungeonData.getRewardPreviewData() == null
|| dungeonData.getRewardPreviewData().getPreviewItems().length == 0) {
public boolean getStatueDrops(Player player, boolean useCondensed, int groupId) { return false;
if (!isFinishedSuccessfully() || dungeonData.getRewardPreviewData() == null || dungeonData.getRewardPreviewData().getPreviewItems().length == 0) { }
return false;
} // Already rewarded
if (rewardedPlayers.contains(player.getUid())) {
// Already rewarded return false;
if (rewardedPlayers.contains(player.getUid())) { }
return false;
} if (!handleCost(player, useCondensed)) {
return false;
}
if (!handleCost(player, useCondensed)) {
return false; // Get and roll rewards.
} List<GameItem> rewards = new ArrayList<>(this.rollRewards(useCondensed));
// Add rewards to player and send notification.
// Get and roll rewards. player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
List<GameItem> rewards = new ArrayList<>(this.rollRewards(useCondensed)); player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
// Add rewards to player and send notification.
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop); rewardedPlayers.add(player.getUid());
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
scene.getScriptManager().callEvent(new ScriptArgs(groupId, EventType.EVENT_DUNGEON_REWARD_GET));
rewardedPlayers.add(player.getUid()); return true;
}
scene.getScriptManager().callEvent(new ScriptArgs(groupId, EventType.EVENT_DUNGEON_REWARD_GET));
return true; public boolean handleCost(Player player, boolean useCondensed) {
} int resinCost = dungeonData.getStatueCostCount() != 0 ? dungeonData.getStatueCostCount() : 20;
if (resinCost == 0) {
public boolean handleCost(Player player, boolean useCondensed) { return true;
int resinCost = dungeonData.getStatueCostCount() != 0 ? dungeonData.getStatueCostCount() : 20; }
if (resinCost == 0) { if (useCondensed) {
return true; // Check if condensed resin is usable here.
} // For this, we use the following logic for now:
if (useCondensed) { // The normal resin cost of the dungeon has to be 20.
// Check if condensed resin is usable here. if (resinCost != 20) {
// For this, we use the following logic for now: return false;
// The normal resin cost of the dungeon has to be 20. }
if (resinCost != 20) {
return false; // Spend the condensed resin and only proceed if the transaction succeeds.
} return player.getResinManager().useCondensedResin(1);
} else if (dungeonData.getStatueCostID() == 106) {
// Spend the condensed resin and only proceed if the transaction succeeds. // Spend the resin and only proceed if the transaction succeeds.
return player.getResinManager().useCondensedResin(1); return player.getResinManager().useResin(resinCost);
} else if (dungeonData.getStatueCostID() == 106) { }
// Spend the resin and only proceed if the transaction succeeds. return true;
return player.getResinManager().useResin(resinCost); }
}
return true; private List<GameItem> rollRewards(boolean useCondensed) {
} List<GameItem> rewards = new ArrayList<>();
int dungeonId = this.dungeonData.getId();
private List<GameItem> rollRewards(boolean useCondensed) { // If we have specific drop data for this dungeon, we use it.
List<GameItem> rewards = new ArrayList<>(); if (GameData.getDungeonDropDataMap().containsKey(dungeonId)) {
int dungeonId = this.dungeonData.getId(); List<DungeonDropEntry> dropEntries = GameData.getDungeonDropDataMap().get(dungeonId);
// If we have specific drop data for this dungeon, we use it.
if (GameData.getDungeonDropDataMap().containsKey(dungeonId)) { // Roll for each drop group.
List<DungeonDropEntry> dropEntries = GameData.getDungeonDropDataMap().get(dungeonId); for (var entry : dropEntries) {
// Determine the number of drops we get for this entry.
// Roll for each drop group. int start = entry.getCounts().get(0);
for (var entry : dropEntries) { int end = entry.getCounts().get(entry.getCounts().size() - 1);
// Determine the number of drops we get for this entry. var candidateAmounts = IntStream.range(start, end + 1).boxed().collect(Collectors.toList());
int start = entry.getCounts().get(0);
int end = entry.getCounts().get(entry.getCounts().size() - 1); int amount = Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities());
var candidateAmounts = IntStream.range(start, end + 1).boxed().collect(Collectors.toList());
if (useCondensed) {
int amount = Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities()); amount += Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities());
}
if (useCondensed) {
amount += Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities()); // Double rewards in multiplay mode, if specified.
} if (entry.isMpDouble() && this.getScene().getPlayerCount() > 1) {
amount *= 2;
// Double rewards in multiplay mode, if specified. }
if (entry.isMpDouble() && this.getScene().getPlayerCount() > 1) {
amount *= 2; // Roll items for this group.
} // 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
// Roll items for this group. // items,
// Here, we have to handle stacking, or the client will not display results correctly. // we roll them separately. If not, we stack them. This should work out in practice, at
// For now, we use the following logic: If the possible drop item are a list of multiple items, // 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)); } else {
} else { 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));
} }
} }
} }
} }
// Otherwise, we fall back to the preview data. // Otherwise, we fall back to the preview data.
else { else {
Grasscutter.getLogger().info("No drop data found or dungeon {}, falling back to preview data ...", dungeonId); Grasscutter.getLogger()
for (ItemParamData param : dungeonData.getRewardPreviewData().getPreviewItems()) { .info("No drop data found or dungeon {}, falling back to preview data ...", dungeonId);
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1))); for (ItemParamData param : dungeonData.getRewardPreviewData().getPreviewItems()) {
} rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
} }
}
return rewards;
} return rewards;
}
public void applyTrialTeam(Player player) {
if (getDungeonData() == null) return; public void applyTrialTeam(Player player) {
if (getDungeonData() == null) return;
switch (getDungeonData().getType()) {
// case DUNGEON_PLOT is handled by quest execs switch (getDungeonData().getType()) {
case DUNGEON_ACTIVITY -> { // case DUNGEON_PLOT is handled by quest execs
switch (getDungeonData().getPlayType()) { case DUNGEON_ACTIVITY -> {
case DUNGEON_PLAY_TYPE_TRIAL_AVATAR -> { switch (getDungeonData().getPlayType()) {
val activityHandler = player.getActivityManager() case DUNGEON_PLAY_TYPE_TRIAL_AVATAR -> {
.getActivityHandlerAs(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR, TrialAvatarActivityHandler.class); val activityHandler =
activityHandler.ifPresent(trialAvatarActivityHandler -> player
this.trialTeam = trialAvatarActivityHandler.getTrialAvatarDungeonTeam()); .getActivityManager()
} .getActivityHandlerAs(
} ActivityType.NEW_ACTIVITY_TRIAL_AVATAR, TrialAvatarActivityHandler.class);
} activityHandler.ifPresent(
case DUNGEON_ELEMENT_CHALLENGE -> {} // TODO trialAvatarActivityHandler ->
} this.trialTeam = trialAvatarActivityHandler.getTrialAvatarDungeonTeam());
}
if (this.trialTeam != null) { }
player.getTeamManager().addTrialAvatars(trialTeam.trialAvatarIds); }
} case DUNGEON_ELEMENT_CHALLENGE -> {} // TODO
} }
public void unsetTrialTeam(Player player){ if (this.trialTeam != null) {
if (this.trialTeam == null) return; player.getTeamManager().addTrialAvatars(trialTeam.trialAvatarIds);
}
player.getTeamManager().removeTrialAvatar(); }
this.trialTeam = null;
} public void unsetTrialTeam(Player player) {
if (this.trialTeam == null) return;
public void startDungeon() {
this.startSceneTime = scene.getSceneTimeSeconds(); player.getTeamManager().removeTrialAvatar();
scene.getPlayers().forEach(p -> { this.trialTeam = null;
p.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_ENTER_DUNGEON, dungeonData.getId()); }
applyTrialTeam(p);
}); public void startDungeon() {
} this.startSceneTime = scene.getSceneTimeSeconds();
scene
public void finishDungeon() { .getPlayers()
notifyEndDungeon(true); .forEach(
endDungeon(BaseDungeonResult.DungeonEndReason.COMPLETED); p -> {
} p.getQuestManager()
.queueEvent(QuestContent.QUEST_CONTENT_ENTER_DUNGEON, dungeonData.getId());
public void notifyEndDungeon(boolean successfully) { applyTrialTeam(p);
scene.getPlayers().forEach(p -> { });
// Quest trigger }
p.getQuestManager().queueEvent(successfully ?
QuestContent.QUEST_CONTENT_FINISH_DUNGEON : QuestContent.QUEST_CONTENT_FAIL_DUNGEON, public void finishDungeon() {
dungeonData.getId()); notifyEndDungeon(true);
endDungeon(BaseDungeonResult.DungeonEndReason.COMPLETED);
// Battle pass trigger }
if (dungeonData.getType().isCountsToBattlepass() && successfully) {
p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_FINISH_DUNGEON); public void notifyEndDungeon(boolean successfully) {
} scene
}); .getPlayers()
scene.getScriptManager().callEvent(new ScriptArgs(0, EventType.EVENT_DUNGEON_SETTLE, successfully ? 1 : 0)); .forEach(
} p -> {
// Quest trigger
public void quitDungeon() { p.getQuestManager()
notifyEndDungeon(false); .queueEvent(
endDungeon(BaseDungeonResult.DungeonEndReason.QUIT); successfully
} ? QuestContent.QUEST_CONTENT_FINISH_DUNGEON
: QuestContent.QUEST_CONTENT_FAIL_DUNGEON,
public void failDungeon() { dungeonData.getId());
notifyEndDungeon(false);
endDungeon(BaseDungeonResult.DungeonEndReason.FAILED); // Battle pass trigger
} if (dungeonData.getType().isCountsToBattlepass() && successfully) {
p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_FINISH_DUNGEON);
public void endDungeon(BaseDungeonResult.DungeonEndReason endReason) { }
if (scene.getDungeonSettleListeners() != null) { });
scene.getDungeonSettleListeners().forEach(o -> o.onDungeonSettle(this, endReason)); scene
} .getScriptManager()
ended = true; .callEvent(new ScriptArgs(0, EventType.EVENT_DUNGEON_SETTLE, successfully ? 1 : 0));
} }
public void restartDungeon() { public void quitDungeon() {
this.scene.setKilledMonsterCount(0); notifyEndDungeon(false);
this.rewardedPlayers.clear(); endDungeon(BaseDungeonResult.DungeonEndReason.QUIT);
Arrays.fill(finishedConditions, 0); }
this.ended = false;
this.activeDungeonWayPoints.clear(); public void failDungeon() {
} notifyEndDungeon(false);
endDungeon(BaseDungeonResult.DungeonEndReason.FAILED);
public void cleanUpScene() { }
this.scene.setDungeonManager(null);
this.scene.setKilledMonsterCount(0); public void endDungeon(BaseDungeonResult.DungeonEndReason endReason) {
} if (scene.getDungeonSettleListeners() != null) {
} scene.getDungeonSettleListeners().forEach(o -> o.onDungeonSettle(this, endReason));
}
ended = true;
}
public void restartDungeon() {
this.scene.setKilledMonsterCount(0);
this.rewardedPlayers.clear();
Arrays.fill(finishedConditions, 0);
this.ended = false;
this.activeDungeonWayPoints.clear();
}
public void cleanUpScene() {
this.scene.setDungeonManager(null);
this.scene.setKilledMonsterCount(0);
}
}

View File

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

View File

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

View File

@ -1,38 +1,40 @@
package emu.grasscutter.game.dungeons; package emu.grasscutter.game.dungeons;
import emu.grasscutter.game.dungeons.dungeon_results.BaseDungeonResult.DungeonEndReason; 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.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;
public class TowerDungeonSettleListener implements DungeonSettleListener {
public class TowerDungeonSettleListener implements DungeonSettleListener {
@Override
@Override public void onDungeonSettle(DungeonManager dungeonManager, DungeonEndReason endReason) {
public void onDungeonSettle(DungeonManager dungeonManager, DungeonEndReason endReason) { var scene = dungeonManager.getScene();
var scene = dungeonManager.getScene(); var dungeonData = dungeonManager.getDungeonData();
var dungeonData = dungeonManager.getDungeonData(); if (scene.getLoadedGroups().stream()
if (scene.getLoadedGroups().stream().anyMatch(g -> { .anyMatch(
var variables = scene.getScriptManager().getVariables(g.id); g -> {
return variables != null && variables.containsKey("stage") && variables.get("stage") == 1; var variables = scene.getScriptManager().getVariables(g.id);
})) { return variables != null
return; && variables.containsKey("stage")
} && variables.get("stage") == 1;
})) {
var towerManager = scene.getPlayers().get(0).getTowerManager(); return;
}
towerManager.notifyCurLevelRecordChangeWhenDone(3);
scene.broadcastPacket(new PacketTowerFloorRecordChangeNotify( var towerManager = scene.getPlayers().get(0).getTowerManager();
towerManager.getCurrentFloorId(),
3, towerManager.notifyCurLevelRecordChangeWhenDone(3);
towerManager.canEnterScheduleFloor() scene.broadcastPacket(
)); new PacketTowerFloorRecordChangeNotify(
towerManager.getCurrentFloorId(), 3, towerManager.canEnterScheduleFloor()));
var challenge = scene.getChallenge();
var dungeonStats = new DungeonEndStats(scene.getKilledMonsterCount(), challenge.getFinishedTime(), 0, endReason); var challenge = scene.getChallenge();
var result = new TowerResult(dungeonData, dungeonStats, towerManager, challenge); var dungeonStats =
new DungeonEndStats(
scene.broadcastPacket(new PacketDungeonSettleNotify(result)); scene.getKilledMonsterCount(), challenge.getFinishedTime(), 0, endReason);
var result = new TowerResult(dungeonData, dungeonStats, towerManager, challenge);
}
} scene.broadcastPacket(new PacketDungeonSettleNotify(result));
}
}

View File

@ -1,167 +1,178 @@
package emu.grasscutter.game.dungeons.challenge; 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.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.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.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;
import java.util.List; 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 public class WorldChallenge {
public class WorldChallenge { private final Scene scene;
private final Scene scene; private final SceneGroup group;
private final SceneGroup group; private final int challengeId;
private final int challengeId; private final int challengeIndex;
private final int challengeIndex; private final List<Integer> paramList;
private final List<Integer> paramList; private final int timeLimit;
private final int timeLimit; private final List<ChallengeTrigger> challengeTriggers;
private final List<ChallengeTrigger> challengeTriggers; private final int goal;
private final int goal; private final AtomicInteger score;
private final AtomicInteger score; private boolean progress;
private boolean progress; private boolean success;
private boolean success; private long startedAt;
private long startedAt; private int finishedTime;
private int finishedTime;
public WorldChallenge(
public WorldChallenge( Scene scene,
Scene scene, SceneGroup group,
SceneGroup group, int challengeId,
int challengeId, int challengeIndex,
int challengeIndex, List<Integer> paramList,
List<Integer> paramList, int timeLimit,
int timeLimit, int goal,
int goal, List<ChallengeTrigger> challengeTriggers) {
List<ChallengeTrigger> challengeTriggers) { this.scene = scene;
this.scene = scene; this.group = group;
this.group = group; this.challengeId = challengeId;
this.challengeId = challengeId; this.challengeIndex = challengeIndex;
this.challengeIndex = challengeIndex; this.paramList = paramList;
this.paramList = paramList; this.timeLimit = timeLimit;
this.timeLimit = timeLimit; this.challengeTriggers = challengeTriggers;
this.challengeTriggers = challengeTriggers; this.goal = goal;
this.goal = goal; this.score = new AtomicInteger(0);
this.score = new AtomicInteger(0); }
}
public boolean inProgress() {
public boolean inProgress() { return this.progress;
return this.progress; }
}
public void onCheckTimeOut() {
public void onCheckTimeOut() { if (!inProgress()) {
if (!inProgress()) { return;
return; }
} if (timeLimit <= 0) {
if (timeLimit <= 0) { return;
return; }
} challengeTriggers.forEach(t -> t.onCheckTimeout(this));
challengeTriggers.forEach(t -> t.onCheckTimeout(this)); }
}
public void start() {
public void start() { if (inProgress()) {
if (inProgress()) { Grasscutter.getLogger().info("Could not start a in progress challenge.");
Grasscutter.getLogger().info("Could not start a in progress challenge."); return;
return; }
} this.progress = true;
this.progress = true; this.startedAt = System.currentTimeMillis();
this.startedAt = System.currentTimeMillis(); getScene().broadcastPacket(new PacketDungeonChallengeBeginNotify(this));
getScene().broadcastPacket(new PacketDungeonChallengeBeginNotify(this)); challengeTriggers.forEach(t -> t.onBegin(this));
challengeTriggers.forEach(t -> t.onBegin(this)); }
}
public void done() {
public void done() { if (!this.inProgress()) return;
if (!this.inProgress()) return; this.finish(true);
this.finish(true);
var scene = this.getScene();
var scene = this.getScene(); var dungeonManager = scene.getDungeonManager();
var dungeonManager = scene.getDungeonManager(); if (dungeonManager != null && dungeonManager.getDungeonData() != null) {
if (dungeonManager != null && dungeonManager.getDungeonData() != null) { scene
scene.getPlayers().forEach(p -> p.getActivityManager().triggerWatcher( .getPlayers()
WatcherTriggerType.TRIGGER_FINISH_CHALLENGE, .forEach(
String.valueOf(dungeonManager.getDungeonData().getId()), p ->
String.valueOf(this.getGroup().id), p.getActivityManager()
String.valueOf(this.getChallengeId()) .triggerWatcher(
)); WatcherTriggerType.TRIGGER_FINISH_CHALLENGE,
} String.valueOf(dungeonManager.getDungeonData().getId()),
String.valueOf(this.getGroup().id),
scene.getScriptManager().callEvent( String.valueOf(this.getChallengeId())));
// 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()); scene
.getScriptManager()
this.challengeTriggers.forEach(t -> t.onFinish(this)); .callEvent(
} // TODO record the time in PARAM2 and used in action
new ScriptArgs(this.getGroup().id, EventType.EVENT_CHALLENGE_SUCCESS)
public void fail(){ .setParam2(finishedTime));
if (!this.inProgress()) return; this.getScene()
this.finish(true); .triggerDungeonEvent(
DungeonPassConditionType.DUNGEON_COND_FINISH_CHALLENGE,
this.getScene().getScriptManager().callEvent(new ScriptArgs(this.getGroup().id, EventType.EVENT_CHALLENGE_FAIL)); getChallengeId(),
challengeTriggers.forEach(t -> t.onFinish(this)); getChallengeIndex());
}
this.challengeTriggers.forEach(t -> t.onFinish(this));
private void finish(boolean success) { }
this.progress = false;
this.success = success; public void fail() {
this.finishedTime = (int) ((System.currentTimeMillis() - this.startedAt) / 1000L); if (!this.inProgress()) return;
getScene().broadcastPacket(new PacketDungeonChallengeFinishNotify(this)); this.finish(true);
}
this.getScene()
public int increaseScore() { .getScriptManager()
return score.incrementAndGet(); .callEvent(new ScriptArgs(this.getGroup().id, EventType.EVENT_CHALLENGE_FAIL));
} challengeTriggers.forEach(t -> t.onFinish(this));
}
public void onMonsterDeath(EntityMonster monster) {
if (!inProgress()) { private void finish(boolean success) {
return; this.progress = false;
} this.success = success;
if (monster.getGroupId() != getGroup().id) { this.finishedTime = (int) ((System.currentTimeMillis() - this.startedAt) / 1000L);
return; getScene().broadcastPacket(new PacketDungeonChallengeFinishNotify(this));
} }
this.challengeTriggers.forEach(t -> t.onMonsterDeath(this, monster));
} public int increaseScore() {
return score.incrementAndGet();
public void onGadgetDeath(EntityGadget gadget) { }
if (!inProgress()) {
return; public void onMonsterDeath(EntityMonster monster) {
} if (!inProgress()) {
if (gadget.getGroupId() != getGroup().id) { return;
return; }
} if (monster.getGroupId() != getGroup().id) {
this.challengeTriggers.forEach(t -> t.onGadgetDeath(this, gadget)); return;
} }
this.challengeTriggers.forEach(t -> t.onMonsterDeath(this, monster));
public void onGroupTriggerDeath(SceneTrigger trigger) { }
if(!this.inProgress()) return;
public void onGadgetDeath(EntityGadget gadget) {
var triggerGroup = trigger.getCurrentGroup(); if (!inProgress()) {
if (triggerGroup == null || return;
triggerGroup.id != getGroup().id) { }
return; if (gadget.getGroupId() != getGroup().id) {
} return;
}
this.challengeTriggers.forEach(t -> t.onGroupTrigger(this, trigger)); this.challengeTriggers.forEach(t -> t.onGadgetDeath(this, gadget));
} }
public void onGadgetDamage(EntityGadget gadget) { public void onGroupTriggerDeath(SceneTrigger trigger) {
if (!inProgress()) { if (!this.inProgress()) return;
return;
} var triggerGroup = trigger.getCurrentGroup();
if (gadget.getGroupId() != getGroup().id) { if (triggerGroup == null || triggerGroup.id != getGroup().id) {
return; return;
} }
this.challengeTriggers.forEach(t -> t.onGadgetDamage(this, gadget));
} this.challengeTriggers.forEach(t -> t.onGroupTrigger(this, trigger));
} }
public void onGadgetDamage(EntityGadget gadget) {
if (!inProgress()) {
return;
}
if (gadget.getGroupId() != getGroup().id) {
return;
}
this.challengeTriggers.forEach(t -> t.onGadgetDamage(this, gadget));
}
}

View File

@ -1,36 +1,44 @@
package emu.grasscutter.game.dungeons.challenge.factory; package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.data.GameData; 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.List;
import java.util.ArrayList; import lombok.val;
import java.util.List;
public abstract 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 KillAndGuardChallengeFactoryHandler());
challengeFactoryHandlers.add(new KillAndGuardChallengeFactoryHandler()); challengeFactoryHandlers.add(new KillMonsterCountChallengeFactoryHandler());
challengeFactoryHandlers.add(new KillMonsterCountChallengeFactoryHandler()); challengeFactoryHandlers.add(new KillMonsterInTimeChallengeFactoryHandler());
challengeFactoryHandlers.add(new KillMonsterInTimeChallengeFactoryHandler()); challengeFactoryHandlers.add(new KillMonsterTimeChallengeFactoryHandler());
challengeFactoryHandlers.add(new KillMonsterTimeChallengeFactoryHandler()); challengeFactoryHandlers.add(new SurviveChallengeFactoryHandler());
challengeFactoryHandlers.add(new SurviveChallengeFactoryHandler()); challengeFactoryHandlers.add(new TriggerInTimeChallengeFactoryHandler());
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 localChallengeId,
val challengeData = GameData.getDungeonChallengeConfigDataMap().get(challengeDataId); int challengeDataId,
val challengeType = challengeData.getChallengeType(); int param3,
int param4,
for(var handler : challengeFactoryHandlers){ int param5,
if(!handler.isThisType(challengeType)){ int param6,
continue; Scene scene,
} SceneGroup group) {
return handler.build(localChallengeId, challengeDataId, param3, param4, param5, param6, scene, group); val challengeData = GameData.getDungeonChallengeConfigDataMap().get(challengeDataId);
} val challengeType = challengeData.getChallengeType();
return null;
} for (var handler : challengeFactoryHandlers) {
} if (!handler.isThisType(challengeType)) {
continue;
}
return handler.build(
localChallengeId, challengeDataId, param3, param4, param5, param6, scene, group);
}
return null;
}
}

View File

@ -7,5 +7,14 @@ import emu.grasscutter.scripts.data.SceneGroup;
public interface ChallengeFactoryHandler { public interface ChallengeFactoryHandler {
boolean isThisType(ChallengeType challengeType); boolean isThisType(ChallengeType challengeType);
WorldChallenge build(int challengeIndex, 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,18 +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; public class KillAndGuardChallengeFactoryHandler implements ChallengeFactoryHandler {
import static emu.grasscutter.game.dungeons.challenge.enums.ChallengeType.CHALLENGE_KILL_COUNT_GUARD_HP;
public class KillAndGuardChallengeFactoryHandler implements ChallengeFactoryHandler{
@Override @Override
public boolean isThisType(ChallengeType challengeType) { public boolean isThisType(ChallengeType challengeType) {
// ActiveChallenge with 1,188,234101003,12,3030,0 // ActiveChallenge with 1,188,234101003,12,3030,0
@ -20,15 +19,24 @@ public class KillAndGuardChallengeFactoryHandler implements ChallengeFactoryHand
} }
@Override /*TODO check param4 == monstesToKill*/ @Override /*TODO check param4 == monstesToKill*/
public WorldChallenge build(int challengeIndex, int challengeId, int groupId, int monstersToKill, int gadgetCFGId, int unused, Scene scene, SceneGroup group) { public WorldChallenge build(
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, realGroup, scene,
realGroup,
challengeId, // Id challengeId, // Id
challengeIndex, // Index challengeIndex, // Index
List.of(monstersToKill, 0), List.of(monstersToKill, 0),
0, // Limit 0, // Limit
monstersToKill, // Goal monstersToKill, // Goal
List.of(new KillMonsterCountTrigger(), new GuardTrigger(gadgetCFGId))); List.of(new KillMonsterCountTrigger(), new GuardTrigger(gadgetCFGId)));
} }
} }

View File

@ -5,11 +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) {
// ActiveChallenge with 1, 1, 241033003, 15, 0, 0 // ActiveChallenge with 1, 1, 241033003, 15, 0, 0
@ -17,16 +16,24 @@ public class KillMonsterCountChallengeFactoryHandler implements ChallengeFactory
} }
@Override @Override
public WorldChallenge build(int challengeIndex, int challengeId, int groupId, int goal, int param5, int param6, Scene scene, SceneGroup group) { public WorldChallenge build(
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, realGroup, scene,
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

@ -1,33 +1,40 @@
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.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.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 lombok.val; import java.util.List;
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) { // ActiveChallenge with 180, 72, 240, 133220161, 133220161, 0
// ActiveChallenge with 180, 72, 240, 133220161, 133220161, 0 return challengeType == ChallengeType.CHALLENGE_KILL_MONSTER_IN_TIME;
return challengeType == ChallengeType.CHALLENGE_KILL_MONSTER_IN_TIME; }
}
@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,
val realGroup = scene.getScriptManager().getGroupById(groupId); int challengeId,
return new WorldChallenge( int timeLimit,
scene, realGroup, int groupId,
challengeId, // Id int targetCfgId,
challengeIndex, // Index int param6,
List.of(timeLimit), Scene scene,
timeLimit, // Limit SceneGroup group) {
0, // Goal val realGroup = scene.getScriptManager().getGroupById(groupId);
List.of(new KillMonsterTrigger(targetCfgId), new InTimeTrigger()) return new WorldChallenge(
); scene,
} realGroup,
} challengeId, // Id
challengeIndex, // Index
List.of(timeLimit),
timeLimit, // Limit
0, // Goal
List.of(new KillMonsterTrigger(targetCfgId), new InTimeTrigger()));
}
}

View File

@ -6,30 +6,37 @@ 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(int challengeIndex, int challengeId, int timeLimit, int groupId, int targetCount, int param6, Scene scene, SceneGroup group) { public WorldChallenge build(
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, realGroup, scene,
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,15 +1,14 @@
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) {
@ -19,15 +18,23 @@ public class SurviveChallengeFactoryHandler implements ChallengeFactoryHandler {
} }
@Override @Override
public WorldChallenge build(int challengeIndex, int challengeId, int timeToSurvive, int unused4, int unused5, int unused6, Scene scene, SceneGroup group) { public WorldChallenge build(
int challengeIndex,
int challengeId,
int timeToSurvive,
int unused4,
int unused5,
int unused6,
Scene scene,
SceneGroup group) {
return new WorldChallenge( return new WorldChallenge(
scene, group, scene,
challengeId, // Id group,
challengeIndex, // Index challengeId, // Id
List.of(timeToSurvive), challengeIndex, // Index
timeToSurvive, // Limit List.of(timeToSurvive),
0, // Goal timeToSurvive, // Limit
List.of(new ForTimeTrigger()) 0, // Goal
); List.of(new ForTimeTrigger()));
} }
} }

View File

@ -1,16 +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_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) {
@ -22,15 +21,23 @@ public class TriggerInTimeChallengeFactoryHandler implements ChallengeFactoryHan
} }
@Override @Override
public WorldChallenge build(int challengeIndex, int challengeId, int timeLimit, int param4, int triggerTag, int triggerCount, Scene scene, SceneGroup group) { public WorldChallenge build(
int challengeIndex,
int challengeId,
int timeLimit,
int param4,
int triggerTag,
int triggerCount,
Scene scene,
SceneGroup group) {
return new WorldChallenge( return new WorldChallenge(
scene, group, scene,
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

@ -1,16 +1,22 @@
package emu.grasscutter.game.dungeons.challenge.trigger; 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; 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 onMonsterDeath(WorldChallenge challenge, EntityMonster monster) { } public void onFinish(WorldChallenge challenge) {}
public void onGadgetDeath(WorldChallenge challenge, EntityGadget gadget) { }
public void onCheckTimeout(WorldChallenge challenge) { } public void onMonsterDeath(WorldChallenge challenge, EntityMonster monster) {}
public void onGadgetDamage(WorldChallenge challenge, EntityGadget gadget) { }
public void onGroupTrigger(WorldChallenge challenge, SceneTrigger trigger) { } public void onGadgetDeath(WorldChallenge challenge, EntityGadget gadget) {}
}
public void onCheckTimeout(WorldChallenge challenge) {}
public void onGadgetDamage(WorldChallenge challenge, EntityGadget gadget) {}
public void onGroupTrigger(WorldChallenge challenge, SceneTrigger trigger) {}
}

View File

@ -1,38 +1,38 @@
package emu.grasscutter.game.dungeons.challenge.trigger; 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 ChallengeTrigger {
public class GuardTrigger extends ChallengeTrigger { private final int entityToProtectCFGId;
private final int entityToProtectCFGId; private int lastSendPercent = 100;
private int lastSendPercent = 100;
public GuardTrigger(int entityToProtectCFGId){ public GuardTrigger(int entityToProtectCFGId) {
this.entityToProtectCFGId = entityToProtectCFGId; this.entityToProtectCFGId = entityToProtectCFGId;
} }
public void onBegin(WorldChallenge challenge) { public void onBegin(WorldChallenge 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){ if (gadget.getConfigId() != entityToProtectCFGId) {
return; 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) { if (percent != lastSendPercent) {
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, percent)); challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, percent));
lastSendPercent = percent; lastSendPercent = percent;
} }
if(percent <= 0){ if (percent <= 0) {
challenge.fail(); challenge.fail();
} }
} }
} }

View File

@ -1,22 +1,25 @@
package emu.grasscutter.game.dungeons.challenge.trigger; 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; import lombok.AllArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
public class KillMonsterTrigger extends ChallengeTrigger{ public class KillMonsterTrigger extends ChallengeTrigger {
private int monsterCfgId; private int monsterCfgId;
@Override
public void onBegin(WorldChallenge challenge) { @Override
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 1, challenge.getScore().get())); public void onBegin(WorldChallenge challenge) {
} challenge
.getScene()
@Override .broadcastPacket(new PacketChallengeDataNotify(challenge, 1, challenge.getScore().get()));
public void onMonsterDeath(WorldChallenge challenge, EntityMonster monster) { }
if(monster.getConfigId() == monsterCfgId){
challenge.done(); @Override
} public void onMonsterDeath(WorldChallenge challenge, EntityMonster monster) {
} if (monster.getConfigId() == monsterCfgId) {
} challenge.done();
}
}
}

View File

@ -1,370 +1,370 @@
package emu.grasscutter.game.entity; package emu.grasscutter.game.entity;
import emu.grasscutter.GameConstants; import emu.grasscutter.GameConstants;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.avatar.AvatarData; import emu.grasscutter.data.excels.avatar.AvatarData;
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData; import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.inventory.EquipType; import emu.grasscutter.game.inventory.EquipType;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
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.FightProperty; import emu.grasscutter.game.props.FightProperty;
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.net.proto.AbilityControlBlockOuterClass.AbilityControlBlock; import emu.grasscutter.net.proto.AbilityControlBlockOuterClass.AbilityControlBlock;
import emu.grasscutter.net.proto.AbilityEmbryoOuterClass.AbilityEmbryo; import emu.grasscutter.net.proto.AbilityEmbryoOuterClass.AbilityEmbryo;
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.ChangeEnergyReasonOuterClass.ChangeEnergyReason; import emu.grasscutter.net.proto.ChangeEnergyReasonOuterClass.ChangeEnergyReason;
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason; import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo; import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData; 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.PlayerDieTypeOuterClass.PlayerDieType; import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason; import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
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.SceneAvatarInfoOuterClass.SceneAvatarInfo; import emu.grasscutter.net.proto.SceneAvatarInfoOuterClass.SceneAvatarInfo;
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.VectorOuterClass.Vector; import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.server.event.player.PlayerMoveEvent; import emu.grasscutter.server.event.player.PlayerMoveEvent;
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper; import emu.grasscutter.utils.ProtoHelper;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.Int2FloatMap; import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter; import lombok.Getter;
import lombok.val; import lombok.val;
public class EntityAvatar extends GameEntity { public class EntityAvatar extends GameEntity {
@Getter private final Avatar avatar; @Getter private final Avatar avatar;
@Getter private PlayerDieType killedType; @Getter private PlayerDieType killedType;
@Getter private int killedBy; @Getter private int killedBy;
public EntityAvatar(Avatar avatar) { public EntityAvatar(Avatar avatar) {
this(null, avatar); this(null, avatar);
} }
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);
var 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 @Override
public int getEntityTypeId() { public int getEntityTypeId() {
return this.getAvatar().getAvatarId(); return this.getAvatar().getAvatarId();
} }
public Player getPlayer() { public Player getPlayer() {
return this.avatar.getPlayer(); return this.avatar.getPlayer();
} }
@Override @Override
public Position getPosition() { public Position getPosition() {
return getPlayer().getPosition(); return getPlayer().getPosition();
} }
@Override @Override
public Position getRotation() { public Position getRotation() {
return getPlayer().getRotation(); return getPlayer().getRotation();
} }
@Override @Override
public boolean isAlive() { public boolean isAlive() {
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f; return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
} }
@Override @Override
public Int2FloatMap getFightProperties() { public Int2FloatMap getFightProperties() {
return getAvatar().getFightProperties(); return getAvatar().getFightProperties();
} }
public int getWeaponEntityId() { public int getWeaponEntityId() {
if (getAvatar().getWeapon() != null) { if (getAvatar().getWeapon() != null) {
return getAvatar().getWeapon().getWeaponEntityId(); return getAvatar().getWeapon().getWeaponEntityId();
} }
return 0; return 0;
} }
@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.
this.killedType = PlayerDieType.PLAYER_DIE_TYPE_KILL_BY_MONSTER; this.killedType = PlayerDieType.PLAYER_DIE_TYPE_KILL_BY_MONSTER;
this.killedBy = killerId; this.killedBy = killerId;
clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE); clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE);
} }
public void onDeath(PlayerDieType dieType, int killerId) { public void onDeath(PlayerDieType dieType, int killerId) {
super.onDeath(killerId); // Invoke super class's onDeath() method. super.onDeath(killerId); // Invoke super class's onDeath() method.
this.killedType = dieType; this.killedType = dieType;
this.killedBy = killerId; this.killedBy = killerId;
clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE); clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE);
} }
@Override @Override
public float heal(float amount) { public float heal(float amount) {
// Do not heal character if they are dead // Do not heal character if they are dead
if (!this.isAlive()) { if (!this.isAlive()) {
return 0f; return 0f;
} }
float healed = super.heal(amount); float healed = super.heal(amount);
if (healed > 0f) { if (healed > 0f) {
getScene() getScene()
.broadcastPacket( .broadcastPacket(
new PacketEntityFightPropChangeReasonNotify( new PacketEntityFightPropChangeReasonNotify(
this, this,
FightProperty.FIGHT_PROP_CUR_HP, FightProperty.FIGHT_PROP_CUR_HP,
healed, healed,
PropChangeReason.PROP_CHANGE_REASON_ABILITY, PropChangeReason.PROP_CHANGE_REASON_ABILITY,
ChangeHpReason.CHANGE_HP_REASON_ADD_ABILITY)); ChangeHpReason.CHANGE_HP_REASON_ADD_ABILITY));
} }
return healed; return healed;
} }
public void clearEnergy(ChangeEnergyReason reason) { public void clearEnergy(ChangeEnergyReason reason) {
// Fight props. // Fight props.
val curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp(); val curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
float curEnergy = this.getFightProperty(curEnergyProp); float curEnergy = this.getFightProperty(curEnergyProp);
// Set energy to zero. // Set energy to zero.
this.avatar.setCurrentEnergy(curEnergyProp, 0); this.avatar.setCurrentEnergy(curEnergyProp, 0);
// Send packets. // Send packets.
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp)); this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp));
if (reason == ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START) { if (reason == ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START) {
this.getScene() this.getScene()
.broadcastPacket( .broadcastPacket(
new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -curEnergy, reason)); new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -curEnergy, reason));
} }
} }
/** /**
* Adds a fixed amount of energy to the current avatar. * Adds a fixed amount of energy to the current avatar.
* *
* @param amount The amount of energy to add. * @param amount The amount of energy to add.
* @return True if the energy was added, false if the energy was not added. * @return True if the energy was added, false if the energy was not added.
*/ */
public boolean addEnergy(float amount) { public boolean addEnergy(float amount) {
var curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp(); var curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
var curEnergy = this.getFightProperty(curEnergyProp); var curEnergy = this.getFightProperty(curEnergyProp);
if (curEnergy == amount) return false; if (curEnergy == amount) return false;
this.getAvatar().setCurrentEnergy(curEnergyProp, amount); this.getAvatar().setCurrentEnergy(curEnergyProp, amount);
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp)); this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp));
return true; 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);
} }
public void addEnergy(float amount, PropChangeReason reason, boolean isFlat) { public void addEnergy(float amount, PropChangeReason reason, boolean isFlat) {
// Get current and maximum energy for this avatar. // Get current and maximum energy for this avatar.
val elementType = this.getAvatar().getSkillDepot().getElementType(); val elementType = this.getAvatar().getSkillDepot().getElementType();
val curEnergyProp = elementType.getCurEnergyProp(); val curEnergyProp = elementType.getCurEnergyProp();
val maxEnergyProp = elementType.getMaxEnergyProp(); val maxEnergyProp = elementType.getMaxEnergyProp();
float curEnergy = this.getFightProperty(curEnergyProp); float curEnergy = this.getFightProperty(curEnergyProp);
float maxEnergy = this.getFightProperty(maxEnergyProp); float maxEnergy = this.getFightProperty(maxEnergyProp);
// Scale amount by energy recharge, if the amount is not flat. // Scale amount by energy recharge, if the amount is not flat.
if (!isFlat) { if (!isFlat) {
amount *= this.getFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY); amount *= this.getFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY);
} }
// Determine the new energy value. // Determine the new energy value.
float newEnergy = Math.min(curEnergy + amount, maxEnergy); float newEnergy = Math.min(curEnergy + amount, maxEnergy);
// Set energy and notify. // Set energy and notify.
if (newEnergy != curEnergy) { if (newEnergy != curEnergy) {
this.avatar.setCurrentEnergy(curEnergyProp, newEnergy); this.avatar.setCurrentEnergy(curEnergyProp, newEnergy);
this.getScene() this.getScene()
.broadcastPacket(new PacketAvatarFightPropUpdateNotify(this.getAvatar(), curEnergyProp)); .broadcastPacket(new PacketAvatarFightPropUpdateNotify(this.getAvatar(), curEnergyProp));
this.getScene() this.getScene()
.broadcastPacket( .broadcastPacket(
new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, newEnergy, reason)); new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, newEnergy, reason));
} }
} }
public SceneAvatarInfo getSceneAvatarInfo() { public SceneAvatarInfo getSceneAvatarInfo() {
val avatar = this.getAvatar(); val avatar = this.getAvatar();
val player = this.getPlayer(); val player = this.getPlayer();
SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.Builder avatarInfo =
SceneAvatarInfo.newBuilder() SceneAvatarInfo.newBuilder()
.setUid(player.getUid()) .setUid(player.getUid())
.setAvatarId(avatar.getAvatarId()) .setAvatarId(avatar.getAvatarId())
.setGuid(avatar.getGuid()) .setGuid(avatar.getGuid())
.setPeerId(player.getPeerId()) .setPeerId(player.getPeerId())
.addAllTalentIdList(avatar.getTalentIdList()) .addAllTalentIdList(avatar.getTalentIdList())
.setCoreProudSkillLevel(avatar.getCoreProudSkillLevel()) .setCoreProudSkillLevel(avatar.getCoreProudSkillLevel())
.putAllSkillLevelMap(avatar.getSkillLevelMap()) .putAllSkillLevelMap(avatar.getSkillLevelMap())
.setSkillDepotId(avatar.getSkillDepotId()) .setSkillDepotId(avatar.getSkillDepotId())
.addAllInherentProudSkillList(avatar.getProudSkillList()) .addAllInherentProudSkillList(avatar.getProudSkillList())
.putAllProudSkillExtraLevelMap(avatar.getProudSkillBonusMap()) .putAllProudSkillExtraLevelMap(avatar.getProudSkillBonusMap())
.addAllTeamResonanceList(player.getTeamManager().getTeamResonances()) .addAllTeamResonanceList(player.getTeamManager().getTeamResonances())
.setWearingFlycloakId(avatar.getFlyCloak()) .setWearingFlycloakId(avatar.getFlyCloak())
.setCostumeId(avatar.getCostume()) .setCostumeId(avatar.getCostume())
.setBornTime(avatar.getBornTime()); .setBornTime(avatar.getBornTime());
for (GameItem item : avatar.getEquips().values()) { for (GameItem item : avatar.getEquips().values()) {
if (item.getItemData().getEquipType() == EquipType.EQUIP_WEAPON) { if (item.getItemData().getEquipType() == EquipType.EQUIP_WEAPON) {
avatarInfo.setWeapon(item.createSceneWeaponInfo()); avatarInfo.setWeapon(item.createSceneWeaponInfo());
} else { } else {
avatarInfo.addReliquaryList(item.createSceneReliquaryInfo()); avatarInfo.addReliquaryList(item.createSceneReliquaryInfo());
} }
avatarInfo.addEquipIdList(item.getItemId()); avatarInfo.addEquipIdList(item.getItemId());
} }
return avatarInfo.build(); return avatarInfo.build();
} }
@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(Vector.newBuilder())) SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
.setBornPos(Vector.newBuilder()) .setBornPos(Vector.newBuilder())
.build(); .build();
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.Builder entityInfo =
SceneEntityInfo.newBuilder() SceneEntityInfo.newBuilder()
.setEntityId(getId()) .setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_AVATAR) .setEntityType(ProtEntityType.PROT_ENTITY_TYPE_AVATAR)
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder()) .addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder()) .setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority) .setEntityAuthorityInfo(authority)
.setLastMoveSceneTimeMs(this.getLastMoveSceneTimeMs()) .setLastMoveSceneTimeMs(this.getLastMoveSceneTimeMs())
.setLastMoveReliableSeq(this.getLastMoveReliableSeq()) .setLastMoveReliableSeq(this.getLastMoveReliableSeq())
.setLifeState(this.getLifeState().getValue()); .setLifeState(this.getLifeState().getValue());
if (this.getScene() != null) { if (this.getScene() != null) {
entityInfo.setMotionInfo(this.getMotionInfo()); entityInfo.setMotionInfo(this.getMotionInfo());
} }
this.addAllFightPropsToEntityInfo(entityInfo); this.addAllFightPropsToEntityInfo(entityInfo);
PropPair pair = PropPair pair =
PropPair.newBuilder() PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId()) .setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue( .setPropValue(
ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getAvatar().getLevel())) ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getAvatar().getLevel()))
.build(); .build();
entityInfo.addPropList(pair); entityInfo.addPropList(pair);
entityInfo.setAvatar(this.getSceneAvatarInfo()); entityInfo.setAvatar(this.getSceneAvatarInfo());
return entityInfo.build(); return entityInfo.build();
} }
public AbilityControlBlock getAbilityControlBlock() { public AbilityControlBlock getAbilityControlBlock() {
AvatarData data = this.getAvatar().getAvatarData(); AvatarData data = this.getAvatar().getAvatarData();
AbilityControlBlock.Builder abilityControlBlock = AbilityControlBlock.newBuilder(); AbilityControlBlock.Builder abilityControlBlock = AbilityControlBlock.newBuilder();
int embryoId = 0; int embryoId = 0;
// Add avatar abilities // Add avatar abilities
if (data.getAbilities() != null) { if (data.getAbilities() != null) {
for (int id : data.getAbilities()) { for (int id : data.getAbilities()) {
AbilityEmbryo emb = AbilityEmbryo emb =
AbilityEmbryo.newBuilder() AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId) .setAbilityId(++embryoId)
.setAbilityNameHash(id) .setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME) .setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build(); .build();
abilityControlBlock.addAbilityEmbryoList(emb); abilityControlBlock.addAbilityEmbryoList(emb);
} }
} }
// Add default abilities // Add default abilities
for (int id : GameConstants.DEFAULT_ABILITY_HASHES) { for (int id : GameConstants.DEFAULT_ABILITY_HASHES) {
AbilityEmbryo emb = AbilityEmbryo emb =
AbilityEmbryo.newBuilder() AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId) .setAbilityId(++embryoId)
.setAbilityNameHash(id) .setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME) .setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build(); .build();
abilityControlBlock.addAbilityEmbryoList(emb); abilityControlBlock.addAbilityEmbryoList(emb);
} }
// Add team resonances // Add team resonances
for (int id : this.getPlayer().getTeamManager().getTeamResonancesConfig()) { for (int id : this.getPlayer().getTeamManager().getTeamResonancesConfig()) {
AbilityEmbryo emb = AbilityEmbryo emb =
AbilityEmbryo.newBuilder() AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId) .setAbilityId(++embryoId)
.setAbilityNameHash(id) .setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME) .setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build(); .build();
abilityControlBlock.addAbilityEmbryoList(emb); abilityControlBlock.addAbilityEmbryoList(emb);
} }
// Add skill depot abilities // Add skill depot abilities
AvatarSkillDepotData skillDepot = AvatarSkillDepotData skillDepot =
GameData.getAvatarSkillDepotDataMap().get(this.getAvatar().getSkillDepotId()); GameData.getAvatarSkillDepotDataMap().get(this.getAvatar().getSkillDepotId());
if (skillDepot != null && skillDepot.getAbilities() != null) { if (skillDepot != null && skillDepot.getAbilities() != null) {
for (int id : skillDepot.getAbilities()) { for (int id : skillDepot.getAbilities()) {
AbilityEmbryo emb = AbilityEmbryo emb =
AbilityEmbryo.newBuilder() AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId) .setAbilityId(++embryoId)
.setAbilityNameHash(id) .setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME) .setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build(); .build();
abilityControlBlock.addAbilityEmbryoList(emb); abilityControlBlock.addAbilityEmbryoList(emb);
} }
} }
// Add equip abilities // Add equip abilities
if (this.getAvatar().getExtraAbilityEmbryos().size() > 0) { if (this.getAvatar().getExtraAbilityEmbryos().size() > 0) {
for (String skill : this.getAvatar().getExtraAbilityEmbryos()) { for (String skill : this.getAvatar().getExtraAbilityEmbryos()) {
AbilityEmbryo emb = AbilityEmbryo emb =
AbilityEmbryo.newBuilder() AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId) .setAbilityId(++embryoId)
.setAbilityNameHash(Utils.abilityHash(skill)) .setAbilityNameHash(Utils.abilityHash(skill))
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME) .setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build(); .build();
abilityControlBlock.addAbilityEmbryoList(emb); abilityControlBlock.addAbilityEmbryoList(emb);
} }
} }
// //
return abilityControlBlock.build(); return abilityControlBlock.build();
} }
/** /**
* Move this entity to a new position. Additionally invoke player move event. * Move this entity to a new position. Additionally invoke player move event.
* *
* @param newPosition The new position. * @param newPosition The new position.
* @param rotation The new rotation. * @param rotation The new rotation.
*/ */
@Override @Override
public void move(Position newPosition, Position rotation) { public void move(Position newPosition, Position rotation) {
// Invoke player move event. // Invoke player move event.
PlayerMoveEvent event = PlayerMoveEvent event =
new PlayerMoveEvent( new PlayerMoveEvent(
this.getPlayer(), PlayerMoveEvent.MoveType.PLAYER, this.getPosition(), newPosition); this.getPlayer(), PlayerMoveEvent.MoveType.PLAYER, this.getPosition(), newPosition);
event.call(); event.call();
// Set position and rotation. // Set position and rotation.
super.move(event.getDestination(), rotation); super.move(event.getDestination(), rotation);
} }
} }

View File

@ -1,63 +1,63 @@
package emu.grasscutter.game.entity; package emu.grasscutter.game.entity;
import emu.grasscutter.data.binout.config.ConfigEntityGadget; 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;
import lombok.Getter; import lombok.Getter;
public abstract class EntityBaseGadget extends GameEntity { public abstract class EntityBaseGadget extends GameEntity {
@Getter(onMethod_ = @Override) @Getter(onMethod_ = @Override)
protected final Position position; protected final Position position;
@Getter(onMethod_ = @Override) @Getter(onMethod_ = @Override)
protected final Position rotation; protected final Position rotation;
public EntityBaseGadget(Scene scene) { public EntityBaseGadget(Scene scene) {
this(scene, null, null); this(scene, null, null);
} }
public EntityBaseGadget(Scene scene, Position position, Position rotation) { public EntityBaseGadget(Scene scene, Position position, Position rotation) {
super(scene); super(scene);
this.position = position != null ? position.clone() : new Position(); this.position = position != null ? position.clone() : new Position();
this.rotation = rotation != null ? rotation.clone() : new Position(); this.rotation = rotation != null ? rotation.clone() : new Position();
} }
public abstract int getGadgetId(); public abstract int getGadgetId();
@Override @Override
public int getEntityTypeId() { public int getEntityTypeId() {
return this.getGadgetId(); 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(ConfigEntityGadget configGadget) { protected void fillFightProps(ConfigEntityGadget configGadget) {
if (configGadget == null || configGadget.getCombat() == null) { if (configGadget == null || configGadget.getCombat() == null) {
return; return;
} }
var combatData = configGadget.getCombat(); var combatData = configGadget.getCombat();
var combatProperties = combatData.getProperty(); var combatProperties = combatData.getProperty();
var targetHp = combatProperties.getHP(); var targetHp = combatProperties.getHP();
setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, targetHp); setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, targetHp);
setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, targetHp); setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, targetHp);
if (combatProperties.isInvincible()) { if (combatProperties.isInvincible()) {
targetHp = Float.POSITIVE_INFINITY; targetHp = Float.POSITIVE_INFINITY;
} }
setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, targetHp); setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, targetHp);
var atk = combatProperties.getAttack(); var atk = combatProperties.getAttack();
setFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, atk); setFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, atk);
setFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK, atk); setFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK, atk);
var def = combatProperties.getDefence(); var def = combatProperties.getDefence();
setFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE, def); setFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE, def);
setFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE, def); setFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE, def);
setLockHP(combatProperties.isLockHP()); setLockHP(combatProperties.isLockHP());
} }
} }

View File

@ -1,281 +1,308 @@
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.config.ConfigEntityGadget; 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.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.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;
import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData; 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.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.net.proto.VisionTypeOuterClass;
import emu.grasscutter.scripts.EntityControllerScriptManager; 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.PacketPlatformStartRouteNotify;
import emu.grasscutter.server.packet.send.PacketPlatformStopRouteNotify; import emu.grasscutter.server.packet.send.PacketPlatformStopRouteNotify;
import emu.grasscutter.server.packet.send.PacketSceneTimeNotify; 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 lombok.Getter; import java.util.ArrayList;
import lombok.Setter; import java.util.List;
import lombok.ToString; import javax.annotation.Nullable;
import lombok.Getter;
import javax.annotation.Nullable; import lombok.Setter;
import java.util.ArrayList; import lombok.ToString;
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))
private int gadgetId; @Setter
@Getter private final Position bornPos; private int gadgetId;
@Getter private final Position bornRot;
@Getter @Setter private GameEntity owner = null; @Getter private final Position bornPos;
@Getter @Setter private List<GameEntity> children = new ArrayList<>(); @Getter private final Position bornRot;
@Getter @Setter private GameEntity owner = null;
@Getter private int state; @Getter @Setter private List<GameEntity> children = new ArrayList<>();
@Getter @Setter private int pointType;
@Getter private GadgetContent content; @Getter private int state;
@Getter(onMethod = @__(@Override), lazy = true) @Getter @Setter private int pointType;
private final Int2FloatMap fightProperties = new Int2FloatOpenHashMap(); @Getter private GadgetContent content;
@Getter @Setter private SceneGadget metaGadget;
@Nullable @Getter @Getter(onMethod = @__(@Override), lazy = true)
private ConfigEntityGadget configGadget; private final Int2FloatMap fightProperties = new Int2FloatOpenHashMap();
@Getter @Setter private BaseRoute routeConfig;
@Getter @Setter private SceneGadget metaGadget;
@Getter @Setter private int stopValue = 0; //Controller related, inited to zero @Nullable @Getter private ConfigEntityGadget configGadget;
@Getter @Setter private int startValue = 0; //Controller related, inited to zero @Getter @Setter private BaseRoute routeConfig;
@Getter @Setter private int ticksSinceChange;
@Getter @Setter private int stopValue = 0; // Controller related, inited to zero
@Getter @Setter private int startValue = 0; // Controller related, inited to zero
public EntityGadget(Scene scene, int gadgetId, Position pos) { @Getter @Setter private int ticksSinceChange;
this(scene, gadgetId, pos, null, null);
} public EntityGadget(Scene scene, int gadgetId, Position pos) {
this(scene, gadgetId, pos, null, null);
public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot) { }
this(scene, gadgetId, pos, rot, null);
} public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot) {
this(scene, gadgetId, pos, rot, null);
public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot, GadgetContent content) { }
super(scene, pos, rot);
public EntityGadget(
this.gadgetData = GameData.getGadgetDataMap().get(gadgetId); Scene scene, int gadgetId, Position pos, Position rot, GadgetContent content) {
if (gadgetData != null && gadgetData.getJsonName() != null) { super(scene, pos, rot);
this.configGadget = GameData.getGadgetConfigData().get(gadgetData.getJsonName());
} this.gadgetData = GameData.getGadgetDataMap().get(gadgetId);
if (gadgetData != null && gadgetData.getJsonName() != null) {
this.id = this.getScene().getWorld().getNextEntityId(EntityIdType.GADGET); this.configGadget = GameData.getGadgetConfigData().get(gadgetData.getJsonName());
this.gadgetId = gadgetId; }
this.content = content;
this.bornPos = this.getPosition().clone(); this.id = this.getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.bornRot = this.getRotation().clone(); this.gadgetId = gadgetId;
this.fillFightProps(configGadget); this.content = content;
this.bornPos = this.getPosition().clone();
if(GameData.getGadgetMappingMap().containsKey(gadgetId)) { this.bornRot = this.getRotation().clone();
String controllerName = GameData.getGadgetMappingMap().get(gadgetId).getServerController(); this.fillFightProps(configGadget);
this.setEntityController(EntityControllerScriptManager.getGadgetController(controllerName));
} 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) { public void setState(int state) {
var instance = getScene().getScriptManager().getCachedGroupInstanceById(metaGadget.group.id); this.state = state;
if(instance != null) instance.cacheGadgetState(metaGadget, 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) { }
if(state == this.getState()) return; //Don't triggers events }
this.setState(state); public void updateState(int state) {
ticksSinceChange = getScene().getSceneTimeSeconds(); if (state == this.getState()) return; // Don't triggers events
this.getScene().broadcastPacket(new PacketGadgetStateNotify(this, state));
getScene().getScriptManager().callEvent(new ScriptArgs(this.getGroupId(), EventType.EVENT_GADGET_STATE_CHANGE, state, this.getConfigId())); this.setState(state);
} ticksSinceChange = getScene().getSceneTimeSeconds();
this.getScene().broadcastPacket(new PacketGadgetStateNotify(this, state));
@Deprecated(forRemoval = true) // Dont use! getScene()
public void setContent(GadgetContent content) { .getScriptManager()
this.content = this.content == null ? content : this.content; .callEvent(
} new ScriptArgs(
this.getGroupId(), EventType.EVENT_GADGET_STATE_CHANGE, state, this.getConfigId()));
// TODO refactor }
public void buildContent() {
if (this.getContent() != null || this.getGadgetData() == null || this.getGadgetData().getType() == null) { @Deprecated(forRemoval = true) // Dont use!
return; public void setContent(GadgetContent content) {
} this.content = this.content == null ? content : this.content;
}
this.content = switch (this.getGadgetData().getType()) {
case GatherPoint -> new GadgetGatherPoint(this); // TODO refactor
case GatherObject -> new GadgetGatherObject(this); public void buildContent() {
case Worktop, SealGadget -> new GadgetWorktop(this); if (this.getContent() != null
case RewardStatue -> new GadgetRewardStatue(this); || this.getGadgetData() == null
case Chest -> new GadgetChest(this); || this.getGadgetData().getType() == null) {
case Gadget -> new GadgetObject(this); return;
default -> null; }
};
} this.content =
switch (this.getGadgetData().getType()) {
@Override case GatherPoint -> new GadgetGatherPoint(this);
public void onInteract(Player player, GadgetInteractReq interactReq) { case GatherObject -> new GadgetGatherObject(this);
if (this.getContent() == null) { case Worktop, SealGadget -> new GadgetWorktop(this);
return; case RewardStatue -> new GadgetRewardStatue(this);
} case Chest -> new GadgetChest(this);
case Gadget -> new GadgetObject(this);
boolean shouldDelete = this.getContent().onInteract(player, interactReq); default -> null;
};
if (shouldDelete) { }
this.getScene().killEntity(this);
} @Override
} public void onInteract(Player player, GadgetInteractReq interactReq) {
if (this.getContent() == null) {
@Override return;
public void onCreate() { }
// Lua event
getScene().getScriptManager().callEvent(new ScriptArgs(this.getGroupId(), EventType.EVENT_GADGET_CREATE, this.getConfigId())); boolean shouldDelete = this.getContent().onInteract(player, interactReq);
}
if (shouldDelete) {
@Override this.getScene().killEntity(this);
public void onRemoved() { }
super.onRemoved(); }
if(!children.isEmpty()) {
getScene().removeEntities(children, VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE); @Override
children.clear(); public void onCreate() {
} // Lua event
} getScene()
.getScriptManager()
@Override .callEvent(
public void onDeath(int killerId) { new ScriptArgs(this.getGroupId(), EventType.EVENT_GADGET_CREATE, this.getConfigId()));
super.onDeath(killerId); // Invoke super class's onDeath() method. }
if (this.getSpawnEntry() != null) { @Override
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry()); public void onRemoved() {
} super.onRemoved();
if (getScene().getChallenge() != null) { if (!children.isEmpty()) {
getScene().getChallenge().onGadgetDeath(this); getScene().removeEntities(children, VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE);
} children.clear();
getScene().getScriptManager().callEvent(new ScriptArgs(this.getGroupId(), EventType.EVENT_ANY_GADGET_DIE, this.getConfigId())); }
}
SceneGroupInstance groupInstance = getScene().getScriptManager().getCachedGroupInstanceById(this.getGroupId());
if(groupInstance != null && metaGadget != null) @Override
groupInstance.getDeadEntities().add(metaGadget.config_id); public void onDeath(int killerId) {
} super.onDeath(killerId); // Invoke super class's onDeath() method.
public boolean startPlatform(){ if (this.getSpawnEntry() != null) {
if(routeConfig == null){ this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
return false; }
} if (getScene().getChallenge() != null) {
getScene().getChallenge().onGadgetDeath(this);
if(routeConfig.isStarted()){ }
return true; getScene()
} .getScriptManager()
getScene().broadcastPacket(new PacketSceneTimeNotify(getScene())); .callEvent(
routeConfig.startRoute(getScene()); new ScriptArgs(this.getGroupId(), EventType.EVENT_ANY_GADGET_DIE, this.getConfigId()));
getScene().broadcastPacket(new PacketPlatformStartRouteNotify(this));
SceneGroupInstance groupInstance =
return true; getScene().getScriptManager().getCachedGroupInstanceById(this.getGroupId());
} if (groupInstance != null && metaGadget != null)
groupInstance.getDeadEntities().add(metaGadget.config_id);
public boolean stopPlatform(){ }
if(routeConfig == null){
return false; public boolean startPlatform() {
} if (routeConfig == null) {
return false;
if(!routeConfig.isStarted()){ }
return true;
} if (routeConfig.isStarted()) {
routeConfig.stopRoute(getScene()); return true;
getScene().broadcastPacket(new PacketPlatformStopRouteNotify(this)); }
getScene().broadcastPacket(new PacketSceneTimeNotify(getScene()));
return true; routeConfig.startRoute(getScene());
} getScene().broadcastPacket(new PacketPlatformStartRouteNotify(this));
@Override return true;
public SceneEntityInfo toProto() { }
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder()) public boolean stopPlatform() {
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder()) if (routeConfig == null) {
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(bornPos.toProto())) return false;
.setBornPos(bornPos.toProto()) }
.build();
if (!routeConfig.isStarted()) {
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder() return true;
.setEntityId(getId()) }
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET) routeConfig.stopRoute(getScene());
.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder())) getScene().broadcastPacket(new PacketPlatformStopRouteNotify(this));
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder()) return true;
.setEntityAuthorityInfo(authority) }
.setLifeState(1);
@Override
PropPair pair = PropPair.newBuilder() public SceneEntityInfo toProto() {
.setType(PlayerProperty.PROP_LEVEL.getId()) EntityAuthorityInfo authority =
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1)) EntityAuthorityInfo.newBuilder()
.build(); .setAbilityInfo(AbilitySyncStateInfo.newBuilder())
entityInfo.addPropList(pair); .setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(
// We do not use the getter to null check because the getter will create a fight prop map if it is null SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(bornPos.toProto()))
if (this.fightProperties != null) { .setBornPos(bornPos.toProto())
addAllFightPropsToEntityInfo(entityInfo); .build();
}
SceneEntityInfo.Builder entityInfo =
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder() SceneEntityInfo.newBuilder()
.setGadgetId(this.getGadgetId()) .setEntityId(getId())
.setGroupId(this.getGroupId()) .setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setConfigId(this.getConfigId()) .setMotionInfo(
.setGadgetState(this.getState()) MotionInfo.newBuilder()
.setIsEnableInteract(true) .setPos(getPosition().toProto())
.setAuthorityPeerId(this.getScene().getWorld().getHostPeerId()); .setRot(getRotation().toProto())
.setSpeed(Vector.newBuilder()))
if (this.metaGadget != null) { .addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
gadgetInfo.setDraftId(this.metaGadget.draft_id); .setEntityClientData(EntityClientData.newBuilder())
} .setEntityAuthorityInfo(authority)
.setLifeState(1);
if(owner != null){
gadgetInfo.setOwnerEntityId(owner.getId()); PropPair pair =
} PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
if (this.getContent() != null) { .setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
this.getContent().onBuildProto(gadgetInfo); .build();
} entityInfo.addPropList(pair);
if(routeConfig!=null){ // We do not use the getter to null check because the getter will create a fight prop map if it
gadgetInfo.setPlatform(getPlatformInfo()); // is null
} if (this.fightProperties != null) {
addAllFightPropsToEntityInfo(entityInfo);
entityInfo.setGadget(gadgetInfo); }
return entityInfo.build(); SceneGadgetInfo.Builder gadgetInfo =
} SceneGadgetInfo.newBuilder()
.setGadgetId(this.getGadgetId())
public PlatformInfoOuterClass.PlatformInfo.Builder getPlatformInfo(){ .setGroupId(this.getGroupId())
if(routeConfig != null){ .setConfigId(this.getConfigId())
return routeConfig.toProto(); .setGadgetState(this.getState())
} .setIsEnableInteract(true)
.setAuthorityPeerId(this.getScene().getWorld().getHostPeerId());
return PlatformInfoOuterClass.PlatformInfo.newBuilder();
} if (this.metaGadget != null) {
} gadgetInfo.setDraftId(this.metaGadget.draft_id);
}
if (owner != null) {
gadgetInfo.setOwnerEntityId(owner.getId());
}
if (this.getContent() != null) {
this.getContent().onBuildProto(gadgetInfo);
}
if (routeConfig != null) {
gadgetInfo.setPlatform(getPlatformInfo());
}
entityInfo.setGadget(gadgetInfo);
return entityInfo.build();
}
public PlatformInfoOuterClass.PlatformInfo.Builder getPlatformInfo() {
if (routeConfig != null) {
return routeConfig.toProto();
}
return PlatformInfoOuterClass.PlatformInfo.newBuilder();
}
}

View File

@ -1,82 +1,82 @@
package emu.grasscutter.game.entity; package emu.grasscutter.game.entity;
import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.*; import emu.grasscutter.net.proto.*;
import emu.grasscutter.scripts.data.SceneNPC; import emu.grasscutter.scripts.data.SceneNPC;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2FloatMap; import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter; import lombok.Getter;
public class EntityNPC extends GameEntity { public class EntityNPC extends GameEntity {
@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;
private final SceneNPC metaNpc; private final SceneNPC metaNpc;
@Getter private final int suiteId; @Getter private final int suiteId;
public EntityNPC(Scene scene, SceneNPC metaNPC, int blockId, int suiteId) { public EntityNPC(Scene scene, SceneNPC metaNPC, int blockId, int suiteId) {
super(scene); super(scene);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.NPC); this.id = getScene().getWorld().getNextEntityId(EntityIdType.NPC);
setConfigId(metaNPC.config_id); setConfigId(metaNPC.config_id);
setGroupId(metaNPC.group.id); setGroupId(metaNPC.group.id);
setBlockId(blockId); setBlockId(blockId);
this.suiteId = suiteId; this.suiteId = suiteId;
this.position = metaNPC.pos.clone(); this.position = metaNPC.pos.clone();
this.rotation = metaNPC.rot.clone(); this.rotation = metaNPC.rot.clone();
this.metaNpc = metaNPC; this.metaNpc = metaNPC;
} }
@Override @Override
public int getEntityTypeId() { public int getEntityTypeId() {
return this.metaNpc.npc_id; return this.metaNpc.npc_id;
} }
@Override @Override
public Int2FloatMap getFightProperties() { public Int2FloatMap getFightProperties() {
return null; return null;
} }
@Override @Override
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() { public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
EntityAuthorityInfoOuterClass.EntityAuthorityInfo authority = EntityAuthorityInfoOuterClass.EntityAuthorityInfo authority =
EntityAuthorityInfoOuterClass.EntityAuthorityInfo.newBuilder() EntityAuthorityInfoOuterClass.EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo.newBuilder()) .setAbilityInfo(AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo( .setRendererChangedInfo(
EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo.newBuilder()) EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo.newBuilder())
.setAiInfo( .setAiInfo(
SceneEntityAiInfoOuterClass.SceneEntityAiInfo.newBuilder() SceneEntityAiInfoOuterClass.SceneEntityAiInfo.newBuilder()
.setIsAiOpen(true) .setIsAiOpen(true)
.setBornPos(getPosition().toProto())) .setBornPos(getPosition().toProto()))
.setBornPos(getPosition().toProto()) .setBornPos(getPosition().toProto())
.build(); .build();
SceneEntityInfoOuterClass.SceneEntityInfo.Builder entityInfo = SceneEntityInfoOuterClass.SceneEntityInfo.Builder entityInfo =
SceneEntityInfoOuterClass.SceneEntityInfo.newBuilder() SceneEntityInfoOuterClass.SceneEntityInfo.newBuilder()
.setEntityId(getId()) .setEntityId(getId())
.setEntityType(ProtEntityTypeOuterClass.ProtEntityType.PROT_ENTITY_TYPE_NPC) .setEntityType(ProtEntityTypeOuterClass.ProtEntityType.PROT_ENTITY_TYPE_NPC)
.setMotionInfo( .setMotionInfo(
MotionInfoOuterClass.MotionInfo.newBuilder() MotionInfoOuterClass.MotionInfo.newBuilder()
.setPos(getPosition().toProto()) .setPos(getPosition().toProto())
.setRot(getRotation().toProto()) .setRot(getRotation().toProto())
.setSpeed(VectorOuterClass.Vector.newBuilder())) .setSpeed(VectorOuterClass.Vector.newBuilder()))
.addAnimatorParaList( .addAnimatorParaList(
AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair
.newBuilder()) .newBuilder())
.setEntityClientData(EntityClientDataOuterClass.EntityClientData.newBuilder()) .setEntityClientData(EntityClientDataOuterClass.EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority) .setEntityAuthorityInfo(authority)
.setLifeState(1); .setLifeState(1);
entityInfo.setNpc( entityInfo.setNpc(
SceneNpcInfoOuterClass.SceneNpcInfo.newBuilder() SceneNpcInfoOuterClass.SceneNpcInfo.newBuilder()
.setNpcId(metaNpc.npc_id) .setNpcId(metaNpc.npc_id)
.setBlockId(getBlockId()) .setBlockId(getBlockId())
.build()); .build());
return entityInfo.build(); return entityInfo.build();
} }
} }

View File

@ -1,96 +1,96 @@
package emu.grasscutter.game.entity; package emu.grasscutter.game.entity;
import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass; import emu.grasscutter.net.proto.SceneEntityInfoOuterClass;
import emu.grasscutter.scripts.data.SceneRegion; import emu.grasscutter.scripts.data.SceneRegion;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2FloatMap; import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import lombok.Getter; import lombok.Getter;
@Getter @Getter
public class EntityRegion extends GameEntity { public class EntityRegion extends GameEntity {
private final Position position; private final Position position;
private final Set<Integer> entities; // Ids of entities inside this region private final Set<Integer> entities; // Ids of entities inside this region
private final SceneRegion metaRegion; private final SceneRegion metaRegion;
private boolean hasNewEntities; private boolean hasNewEntities;
private boolean entityLeave; private boolean entityLeave;
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);
this.setGroupId(region.group.id); this.setGroupId(region.group.id);
this.setBlockId(region.group.block_id); this.setBlockId(region.group.block_id);
this.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 @Override
public int getEntityTypeId() { public int getEntityTypeId() {
return this.metaRegion.config_id; 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;
} }
this.getEntities().add(entity.getId()); this.getEntities().add(entity.getId());
this.hasNewEntities = true; this.hasNewEntities = true;
} }
public boolean hasNewEntities() { public boolean hasNewEntities() {
return hasNewEntities; return hasNewEntities;
} }
public void resetNewEntities() { public void resetNewEntities() {
hasNewEntities = false; hasNewEntities = false;
} }
public void removeEntity(int entityId) { public void removeEntity(int entityId) {
this.getEntities().remove(entityId); this.getEntities().remove(entityId);
this.entityLeave = true; this.entityLeave = true;
} }
public void removeEntity(GameEntity entity) { public void removeEntity(GameEntity entity) {
this.getEntities().remove(entity.getId()); this.getEntities().remove(entity.getId());
this.entityLeave = true; this.entityLeave = true;
} }
public boolean entityLeave() { public boolean entityLeave() {
return this.entityLeave; return this.entityLeave;
} }
public void resetEntityLeave() { public void resetEntityLeave() {
this.entityLeave = false; this.entityLeave = false;
} }
@Override @Override
public Int2FloatMap getFightProperties() { public Int2FloatMap getFightProperties() {
return null; return null;
} }
@Override @Override
public Position getPosition() { public Position getPosition() {
return position; return position;
} }
@Override @Override
public Position getRotation() { public Position getRotation() {
return null; return null;
} }
@Override @Override
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() { public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
/** The Region Entity would not be sent to client. */ /** The Region Entity would not be sent to client. */
return null; return null;
} }
public int getFirstEntityId() { public int getFirstEntityId() {
return entities.stream().findFirst().orElse(0); return entities.stream().findFirst().orElse(0);
} }
} }

View File

@ -1,30 +1,33 @@
package emu.grasscutter.game.entity; package emu.grasscutter.game.entity;
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;
import emu.grasscutter.net.proto.EvtCreateGadgetNotifyOuterClass; import emu.grasscutter.net.proto.EvtCreateGadgetNotifyOuterClass;
import lombok.Getter; 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 EntityGadget platformGadget; @Getter private EntityGadget platformGadget;
public EntitySolarIsotomaClientGadget(Scene scene, Player player, EvtCreateGadgetNotifyOuterClass.EvtCreateGadgetNotify notify) { public EntitySolarIsotomaClientGadget(
super(scene, player, notify); Scene scene, Player player, EvtCreateGadgetNotifyOuterClass.EvtCreateGadgetNotify notify) {
} super(scene, player, notify);
}
@Override
public void onCreate() { @Override
//Create solar isotoma elevator and send to all. public void onCreate() {
this.platformGadget = new EntitySolarIsotomaElevatorPlatform(this, getScene(), ELEVATOR_GADGET_ID, getPosition(), getRotation()); // Create solar isotoma elevator and send to all.
getScene().addEntity(this.platformGadget); this.platformGadget =
} new EntitySolarIsotomaElevatorPlatform(
this, getScene(), ELEVATOR_GADGET_ID, getPosition(), getRotation());
@Override getScene().addEntity(this.platformGadget);
public void onRemoved() { }
//Remove solar isotoma elevator entity.
getScene().removeEntity(this.platformGadget); @Override
} public void onRemoved() {
} // Remove solar isotoma elevator entity.
getScene().removeEntity(this.platformGadget);
}
}

View File

@ -1,112 +1,125 @@
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.config.ConfigEntityGadget; 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;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
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.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;
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo; import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo; import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
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.VehicleInfoOuterClass.VehicleInfo; import emu.grasscutter.net.proto.VehicleInfoOuterClass.VehicleInfo;
import emu.grasscutter.net.proto.VehicleMemberOuterClass.VehicleMember; import emu.grasscutter.net.proto.VehicleMemberOuterClass.VehicleMember;
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 lombok.Getter; import java.util.ArrayList;
import lombok.Setter; import java.util.List;
import javax.annotation.Nullable;
import javax.annotation.Nullable; import lombok.Getter;
import java.util.ArrayList; import lombok.Setter;
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;
@Getter private final int gadgetId; @Getter private final int gadgetId;
@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 ConfigEntityGadget configGadget; @Nullable @Getter private ConfigEntityGadget configGadget;
public EntityVehicle(Scene scene, Player player, int gadgetId, int pointId, Position pos, Position rot) { public EntityVehicle(
super(scene, pos, rot); Scene scene, Player player, int gadgetId, int pointId, Position pos, Position rot) {
this.owner = player; super(scene, pos, rot);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET); this.owner = player;
this.fightProperties = new Int2FloatOpenHashMap(); this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.gadgetId = gadgetId; this.fightProperties = new Int2FloatOpenHashMap();
this.pointId = pointId; this.gadgetId = gadgetId;
this.curStamina = 240; // might be in configGadget.GCALKECLLLP.JBAKBEFIMBN.ANBMPHPOALP this.pointId = pointId;
this.vehicleMembers = new ArrayList<>(); this.curStamina = 240; // might be in configGadget.GCALKECLLLP.JBAKBEFIMBN.ANBMPHPOALP
GadgetData data = GameData.getGadgetDataMap().get(gadgetId); this.vehicleMembers = new ArrayList<>();
if (data != null && data.getJsonName() != null) { GadgetData data = GameData.getGadgetDataMap().get(gadgetId);
this.configGadget = GameData.getGadgetConfigData().get(data.getJsonName()); if (data != null && data.getJsonName() != null) {
} this.configGadget = GameData.getGadgetConfigData().get(data.getJsonName());
}
fillFightProps(configGadget);
} fillFightProps(configGadget);
}
@Override
protected void fillFightProps(ConfigEntityGadget configGadget) { @Override
super.fillFightProps(configGadget); protected void fillFightProps(ConfigEntityGadget configGadget) {
this.addFightProperty(FightProperty.FIGHT_PROP_CUR_SPEED, 0); super.fillFightProps(configGadget);
this.addFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, 0); this.addFightProperty(FightProperty.FIGHT_PROP_CUR_SPEED, 0);
} this.addFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, 0);
}
@Override
public SceneEntityInfo toProto() { @Override
public SceneEntityInfo toProto() {
VehicleInfo vehicle = VehicleInfo.newBuilder()
.setOwnerUid(this.owner.getUid()) VehicleInfo vehicle =
.setCurStamina(getCurStamina()) VehicleInfo.newBuilder()
.build(); .setOwnerUid(this.owner.getUid())
.setCurStamina(getCurStamina())
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder() .build();
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder()) EntityAuthorityInfo authority =
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(getPosition().toProto())) EntityAuthorityInfo.newBuilder()
.setBornPos(getPosition().toProto()) .setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.build(); .setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder() SceneEntityAiInfo.newBuilder()
.setGadgetId(this.getGadgetId()) .setIsAiOpen(true)
.setAuthorityPeerId(this.getOwner().getPeerId()) .setBornPos(getPosition().toProto()))
.setIsEnableInteract(true) .setBornPos(getPosition().toProto())
.setVehicleInfo(vehicle); .build();
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder() SceneGadgetInfo.Builder gadgetInfo =
.setEntityId(getId()) SceneGadgetInfo.newBuilder()
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET) .setGadgetId(this.getGadgetId())
.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder())) .setAuthorityPeerId(this.getOwner().getPeerId())
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder()) .setIsEnableInteract(true)
.setGadget(gadgetInfo) .setVehicleInfo(vehicle);
.setEntityAuthorityInfo(authority)
.setLifeState(1); SceneEntityInfo.Builder entityInfo =
SceneEntityInfo.newBuilder()
PropPair pair = PropPair.newBuilder() .setEntityId(getId())
.setType(PlayerProperty.PROP_LEVEL.getId()) .setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 47)) .setMotionInfo(
.build(); MotionInfo.newBuilder()
.setPos(getPosition().toProto())
this.addAllFightPropsToEntityInfo(entityInfo); .setRot(getRotation().toProto())
entityInfo.addPropList(pair); .setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
return entityInfo.build(); .setGadget(gadgetInfo)
} .setEntityAuthorityInfo(authority)
} .setLifeState(1);
PropPair pair =
PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 47))
.build();
this.addAllFightPropsToEntityInfo(entityInfo);
entityInfo.addPropList(pair);
return entityInfo.build();
}
}

View File

@ -1,264 +1,264 @@
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.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;
import emu.grasscutter.game.world.SpawnDataEntry; import emu.grasscutter.game.world.SpawnDataEntry;
import emu.grasscutter.game.world.World; import emu.grasscutter.game.world.World;
import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair; import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair;
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.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.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;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2FloatMap; import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; 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.Object2FloatMap; import it.unimi.dsi.fastutil.objects.Object2FloatMap;
import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
public abstract class GameEntity { public abstract class GameEntity {
@Getter private final Scene scene; @Getter private final Scene scene;
@Getter protected int id; @Getter protected int id;
@Getter @Setter private SpawnDataEntry spawnEntry; @Getter @Setter private SpawnDataEntry spawnEntry;
@Getter @Setter private int blockId; @Getter @Setter private int blockId;
@Getter @Setter private int configId; @Getter @Setter private int configId;
@Getter @Setter private int groupId; @Getter @Setter private int groupId;
@Getter @Setter private MotionState motionState; @Getter @Setter private MotionState motionState;
@Getter @Setter private int lastMoveSceneTimeMs; @Getter @Setter private int lastMoveSceneTimeMs;
@Getter @Setter private int lastMoveReliableSeq; @Getter @Setter private int lastMoveReliableSeq;
@Getter @Setter private boolean lockHP; @Getter @Setter private boolean lockHP;
// Lua controller for specific actions // Lua controller for specific actions
@Getter @Setter private EntityController entityController; @Getter @Setter private EntityController entityController;
@Getter private ElementType lastAttackType = ElementType.None; @Getter private ElementType lastAttackType = ElementType.None;
// Abilities // Abilities
private Object2FloatMap<String> metaOverrideMap; private Object2FloatMap<String> metaOverrideMap;
private Int2ObjectMap<String> metaModifiers; private Int2ObjectMap<String> metaModifiers;
public GameEntity(Scene scene) { public GameEntity(Scene scene) {
this.scene = scene; this.scene = scene;
this.motionState = MotionState.MOTION_STATE_NONE; this.motionState = MotionState.MOTION_STATE_NONE;
} }
public int getEntityType() { public int getEntityType() {
return this.getId() >> 24; return this.getId() >> 24;
} }
public abstract int getEntityTypeId(); public abstract int getEntityTypeId();
public World getWorld() { public World getWorld() {
return this.getScene().getWorld(); return this.getScene().getWorld();
} }
public boolean isAlive() { public boolean isAlive() {
return true; return true;
} }
public LifeState getLifeState() { public LifeState getLifeState() {
return this.isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD; return this.isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD;
} }
public Object2FloatMap<String> getMetaOverrideMap() { public Object2FloatMap<String> getMetaOverrideMap() {
if (this.metaOverrideMap == null) { if (this.metaOverrideMap == null) {
this.metaOverrideMap = new Object2FloatOpenHashMap<>(); this.metaOverrideMap = new Object2FloatOpenHashMap<>();
} }
return this.metaOverrideMap; return this.metaOverrideMap;
} }
public Int2ObjectMap<String> getMetaModifiers() { public Int2ObjectMap<String> getMetaModifiers() {
if (this.metaModifiers == null) { if (this.metaModifiers == null) {
this.metaModifiers = new Int2ObjectOpenHashMap<>(); this.metaModifiers = new Int2ObjectOpenHashMap<>();
} }
return this.metaModifiers; return this.metaModifiers;
} }
public abstract Int2FloatMap getFightProperties(); public abstract Int2FloatMap getFightProperties();
public abstract Position getPosition(); public abstract Position getPosition();
public abstract Position getRotation(); public abstract Position getRotation();
public void setFightProperty(FightProperty prop, float value) { public void setFightProperty(FightProperty prop, float value) {
this.getFightProperties().put(prop.getId(), value); this.getFightProperties().put(prop.getId(), value);
} }
public void setFightProperty(int id, float value) { public void setFightProperty(int id, float value) {
this.getFightProperties().put(id, value); this.getFightProperties().put(id, value);
} }
public void addFightProperty(FightProperty prop, float value) { public void addFightProperty(FightProperty prop, float value) {
this.getFightProperties().put(prop.getId(), this.getFightProperty(prop) + value); this.getFightProperties().put(prop.getId(), this.getFightProperty(prop) + value);
} }
public float getFightProperty(FightProperty prop) { public float getFightProperty(FightProperty prop) {
return this.getFightProperties().getOrDefault(prop.getId(), 0f); return this.getFightProperties().getOrDefault(prop.getId(), 0f);
} }
public boolean hasFightProperty(FightProperty prop) { public boolean hasFightProperty(FightProperty prop) {
return this.getFightProperties().containsKey(prop.getId()); return this.getFightProperties().containsKey(prop.getId());
} }
public void addAllFightPropsToEntityInfo(SceneEntityInfo.Builder entityInfo) { public void addAllFightPropsToEntityInfo(SceneEntityInfo.Builder entityInfo) {
this.getFightProperties() this.getFightProperties()
.forEach( .forEach(
(key, value) -> { (key, value) -> {
if (key == 0) return; if (key == 0) return;
entityInfo.addFightPropList( entityInfo.addFightPropList(
FightPropPair.newBuilder().setPropType(key).setPropValue(value).build()); FightPropPair.newBuilder().setPropType(key).setPropValue(value).build());
}); });
} }
protected MotionInfo getMotionInfo() { protected MotionInfo getMotionInfo() {
return MotionInfo.newBuilder() return 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();
} }
public float heal(float amount) { public float heal(float amount) {
if (this.getFightProperties() == null) { if (this.getFightProperties() == null) {
return 0f; return 0f;
} }
float curHp = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP); float curHp = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
float maxHp = this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); float maxHp = this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
if (curHp >= maxHp) { if (curHp >= maxHp) {
return 0f; return 0f;
} }
float healed = Math.min(maxHp - curHp, amount); float healed = Math.min(maxHp - curHp, amount);
this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, healed); this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, healed);
this.getScene() this.getScene()
.broadcastPacket( .broadcastPacket(
new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP)); new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
return healed; return healed;
} }
public void damage(float amount) { public void damage(float amount) {
this.damage(amount, 0, ElementType.None); this.damage(amount, 0, ElementType.None);
} }
public void damage(float amount, int killerId, ElementType attackType) { 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;
} }
// Invoke entity damage event. // Invoke entity damage event.
EntityDamageEvent event = EntityDamageEvent event =
new EntityDamageEvent(this, amount, attackType, 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.
} }
float curHp = getFightProperty(FightProperty.FIGHT_PROP_CUR_HP); float curHp = getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
if (curHp != Float.POSITIVE_INFINITY && !lockHP || lockHP && curHp <= event.getDamage()) { if (curHp != Float.POSITIVE_INFINITY && !lockHP || lockHP && curHp <= event.getDamage()) {
// Add negative HP to the current HP property. // Add negative HP to the current HP property.
this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -(event.getDamage())); this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -(event.getDamage()));
} }
// Check if dead // Check if dead
boolean isDead = false; boolean isDead = false;
if (this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) { if (this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) {
this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f); this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
isDead = true; isDead = true;
} }
// Packets // Packets
this.getScene() this.getScene()
.broadcastPacket( .broadcastPacket(
new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP)); new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
// Check if dead. // Check if dead.
if (isDead) { if (isDead) {
this.getScene().killEntity(this, killerId); this.getScene().killEntity(this, killerId);
} }
} }
/** /**
* Runs the Lua callbacks for {@link EntityDamageEvent}. * Runs the Lua callbacks for {@link EntityDamageEvent}.
* *
* @param event The damage event. * @param event The damage event.
*/ */
public void runLuaCallbacks(EntityDamageEvent event) { public void runLuaCallbacks(EntityDamageEvent event) {
if (entityController != null) { if (entityController != null) {
entityController.onBeHurt(this, event.getAttackElementType(), true);//todo is host handling entityController.onBeHurt(this, event.getAttackElementType(), true); // todo is host handling
} }
} }
/** /**
* Move this entity to a new position. * Move this entity to a new position.
* *
* @param position The new position. * @param position The new position.
* @param rotation The new rotation. * @param rotation The new rotation.
*/ */
public void move(Position position, Position rotation) { public void move(Position position, Position rotation) {
// Set the position and rotation. // Set the position and rotation.
this.getPosition().set(position); this.getPosition().set(position);
this.getRotation().set(rotation); this.getRotation().set(rotation);
} }
/** /**
* Called when a player interacts with this entity * Called when a player interacts with this entity
* *
* @param player Player that is interacting with this entity * @param player Player that is interacting with this entity
* @param interactReq Interact request protobuf data * @param interactReq Interact request protobuf data
*/ */
public void onInteract(Player player, GadgetInteractReq interactReq) {} public void onInteract(Player player, GadgetInteractReq interactReq) {}
/** Called when this entity is added to the world */ /** Called when this entity is added to the world */
public void onCreate() {} public void onCreate() {}
public void onRemoved() {} public void onRemoved() {}
public void onTick(int sceneTime) { public void onTick(int sceneTime) {
if (entityController != null) { if (entityController != null) {
entityController.onTimer(this, sceneTime); entityController.onTimer(this, sceneTime);
} }
} }
public int onClientExecuteRequest(int param1, int param2, int param3) { public int onClientExecuteRequest(int param1, int param2, int param3) {
if (entityController != null) { if (entityController != null) {
return entityController.onClientExecuteRequest(this, param1, param2, param3); return entityController.onClientExecuteRequest(this, param1, param2, param3);
} }
return 0; return 0;
} }
/** /**
* Called when this entity dies * Called when this entity dies
* *
* @param killerId Entity id of the entity that killed this entity * @param killerId Entity id of the entity that killed this entity
*/ */
public void onDeath(int killerId) { public void onDeath(int killerId) {
// 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. // Run Lua callbacks.
if (entityController != null) { if (entityController != null) {
entityController.onDie(this, getLastAttackType()); entityController.onDie(this, getLastAttackType());
} }
} }
public abstract SceneEntityInfo toProto(); public abstract SceneEntityInfo toProto();
} }

View File

@ -1,33 +1,34 @@
package emu.grasscutter.game.entity.gadget; package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge; import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
import emu.grasscutter.game.entity.EntityGadget; 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.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 final 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) {
var dungeonManager = player.getScene().getDungeonManager(); var dungeonManager = player.getScene().getDungeonManager();
if (player.getScene().getChallenge() instanceof DungeonChallenge) { if (player.getScene().getChallenge() instanceof DungeonChallenge) {
var useCondensed = req.getResinCostType() == ResinCostTypeOuterClass.ResinCostType.RESIN_COST_TYPE_CONDENSE; var useCondensed =
dungeonManager.getStatueDrops(player, useCondensed, getGadget().getGroupId()); req.getResinCostType() == ResinCostTypeOuterClass.ResinCostType.RESIN_COST_TYPE_CONDENSE;
} dungeonManager.getStatueDrops(player, useCondensed, getGadget().getGroupId());
}
player.sendPacket(
new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_STATUE)); player.sendPacket(
new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_STATUE));
return false;
} return false;
}
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {}
} public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {}
}

View File

@ -1,40 +1,45 @@
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.config.ConfigEntityGadget; import emu.grasscutter.data.binout.config.ConfigEntityGadget;
import emu.grasscutter.game.entity.*; import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.entity.gadget.GadgetAbility; import emu.grasscutter.game.entity.gadget.GadgetAbility;
import emu.grasscutter.game.entity.gadget.platform.AbilityRoute; import emu.grasscutter.game.entity.gadget.platform.AbilityRoute;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
public class EntitySolarIsotomaElevatorPlatform extends EntityGadget { public class EntitySolarIsotomaElevatorPlatform extends EntityGadget {
public EntitySolarIsotomaElevatorPlatform(EntitySolarIsotomaClientGadget isotoma, Scene scene, int gadgetId, Position pos, Position rot) { public EntitySolarIsotomaElevatorPlatform(
super(scene, gadgetId, pos, rot); EntitySolarIsotomaClientGadget isotoma,
setOwner(isotoma); Scene scene,
this.setRouteConfig(new AbilityRoute(rot, false, false, pos)); int gadgetId,
this.setContent(new GadgetAbility(this, isotoma)); Position pos,
} Position rot) {
super(scene, gadgetId, pos, rot);
@Override setOwner(isotoma);
protected void fillFightProps(ConfigEntityGadget configGadget) { this.setRouteConfig(new AbilityRoute(rot, false, false, pos));
if (configGadget == null || configGadget.getCombat() == null) { this.setContent(new GadgetAbility(this, isotoma));
return; }
}
var combatData = configGadget.getCombat(); @Override
var combatProperties = combatData.getProperty(); protected void fillFightProps(ConfigEntityGadget configGadget) {
if (configGadget == null || configGadget.getCombat() == null) {
if (combatProperties.isUseCreatorProperty()) { return;
//If useCreatorProperty == true, use owner's property; }
GameEntity ownerEntity = getOwner(); var combatData = configGadget.getCombat();
if (ownerEntity != null) { var combatProperties = combatData.getProperty();
getFightProperties().putAll(ownerEntity.getFightProperties());
return; if (combatProperties.isUseCreatorProperty()) {
} else { // If useCreatorProperty == true, use owner's property;
Grasscutter.getLogger().warn("Why gadget owner is null?"); GameEntity ownerEntity = getOwner();
} if (ownerEntity != null) {
} getFightProperties().putAll(ownerEntity.getFightProperties());
return;
super.fillFightProps(configGadget); } else {
} Grasscutter.getLogger().warn("Why gadget owner is null?");
} }
}
super.fillFightProps(configGadget);
}
}

View File

@ -1,163 +1,173 @@
package emu.grasscutter.game.mail; package emu.grasscutter.game.mail;
import dev.morphia.annotations.Entity; import static emu.grasscutter.net.proto.MailItemOuterClass.MailItem.*;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Indexed; import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Transient; import dev.morphia.annotations.Id;
import emu.grasscutter.database.DatabaseHelper; import dev.morphia.annotations.Indexed;
import emu.grasscutter.game.player.Player; import dev.morphia.annotations.Transient;
import emu.grasscutter.net.proto.*; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.net.proto.EquipParamOuterClass.EquipParam; import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.MailCollectStateOuterClass.MailCollectState; import emu.grasscutter.net.proto.*;
import emu.grasscutter.net.proto.MailTextContentOuterClass.MailTextContent; import emu.grasscutter.net.proto.EquipParamOuterClass.EquipParam;
import org.bson.types.ObjectId; import emu.grasscutter.net.proto.MailCollectStateOuterClass.MailCollectState;
import emu.grasscutter.net.proto.MailTextContentOuterClass.MailTextContent;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.bson.types.ObjectId;
import static emu.grasscutter.net.proto.MailItemOuterClass.MailItem.*;
@Entity(value = "mail", useDiscriminator = false)
@Entity(value = "mail", useDiscriminator = false) public final class Mail {
public final class Mail { @Id private ObjectId id;
@Id private ObjectId id; @Indexed private int ownerUid;
@Indexed private int ownerUid; public MailContent mailContent;
public MailContent mailContent; public List<MailItem> itemList;
public List<MailItem> itemList; public long sendTime;
public long sendTime; public long expireTime;
public long expireTime; public int importance;
public int importance; public boolean isRead;
public boolean isRead; public boolean isAttachmentGot;
public boolean isAttachmentGot; public int stateValue;
public int stateValue; @Transient private boolean shouldDelete;
@Transient private boolean shouldDelete;
public Mail() {
public Mail() { this(
this(new MailContent(), new ArrayList<MailItem>(), (int) Instant.now().getEpochSecond() + 604800); // TODO: add expire time to send mail command new MailContent(),
} new ArrayList<MailItem>(),
(int) Instant.now().getEpochSecond()
public Mail(MailContent mailContent, List<MailItem> itemList, long expireTime) { + 604800); // TODO: add expire time to send mail command
this(mailContent, itemList, expireTime, 0); }
}
public Mail(MailContent mailContent, List<MailItem> itemList, long expireTime) {
public Mail(MailContent mailContent, List<MailItem> itemList, long expireTime, int importance) { this(mailContent, itemList, expireTime, 0);
this(mailContent, itemList, expireTime, importance, 1); }
}
public Mail(MailContent mailContent, List<MailItem> itemList, long expireTime, int importance) {
public Mail(MailContent mailContent, List<MailItem> itemList, long expireTime, int importance, int state) { this(mailContent, itemList, expireTime, importance, 1);
this.mailContent = mailContent; }
this.itemList = itemList;
this.sendTime = (int) Instant.now().getEpochSecond(); public Mail(
this.expireTime = expireTime; MailContent mailContent,
this.importance = importance; // Starred mail, 0 = No star, 1 = Star. List<MailItem> itemList,
this.isRead = false; long expireTime,
this.isAttachmentGot = false; int importance,
this.stateValue = state; // Different mailboxes, 1 = Default, 3 = Gift-box. int state) {
} this.mailContent = mailContent;
this.itemList = itemList;
public ObjectId getId() { this.sendTime = (int) Instant.now().getEpochSecond();
return id; this.expireTime = expireTime;
} this.importance = importance; // Starred mail, 0 = No star, 1 = Star.
this.isRead = false;
public int getOwnerUid() { this.isAttachmentGot = false;
return ownerUid; this.stateValue = state; // Different mailboxes, 1 = Default, 3 = Gift-box.
} }
public void setOwnerUid(int ownerUid) { public ObjectId getId() {
this.ownerUid = ownerUid; return id;
} }
public MailDataOuterClass.MailData toProto(Player player) { public int getOwnerUid() {
return MailDataOuterClass.MailData.newBuilder() return ownerUid;
.setMailId(player.getMailId(this)) }
.setMailTextContent(this.mailContent.toProto())
.addAllItemList(this.itemList.stream().map(MailItem::toProto).toList()) public void setOwnerUid(int ownerUid) {
.setSendTime((int) this.sendTime) this.ownerUid = ownerUid;
.setExpireTime((int) this.expireTime) }
.setImportance(this.importance)
.setIsRead(this.isRead) public MailDataOuterClass.MailData toProto(Player player) {
.setIsAttachmentGot(this.isAttachmentGot) return MailDataOuterClass.MailData.newBuilder()
.setCollectState(MailCollectState.MAIL_COLLECT_STATE_NOT_COLLECTIBLE) .setMailId(player.getMailId(this))
.build(); .setMailTextContent(this.mailContent.toProto())
} .addAllItemList(this.itemList.stream().map(MailItem::toProto).toList())
.setSendTime((int) this.sendTime)
@Entity .setExpireTime((int) this.expireTime)
public static class MailContent { .setImportance(this.importance)
public String title; .setIsRead(this.isRead)
public String content; .setIsAttachmentGot(this.isAttachmentGot)
public String sender; .setCollectState(MailCollectState.MAIL_COLLECT_STATE_NOT_COLLECTIBLE)
.build();
public MailContent() { }
this.title = "";
this.content = "loading..."; @Entity
this.sender = "loading"; public static class MailContent {
} public String title;
public String content;
public MailContent(String title, String content) { public String sender;
this(title, content, "Server");
} public MailContent() {
this.title = "";
public MailContent(String title, String content, Player sender) { this.content = "loading...";
this(title, content, sender.getNickname()); this.sender = "loading";
} }
public MailContent(String title, String content, String sender) { public MailContent(String title, String content) {
this.title = title; this(title, content, "Server");
this.content = content; }
this.sender = sender;
} public MailContent(String title, String content, Player sender) {
this(title, content, sender.getNickname());
public MailTextContent toProto() { }
return MailTextContent.newBuilder()
.setTitle(this.title) public MailContent(String title, String content, String sender) {
.setContent(this.content) this.title = title;
.setSender(this.sender) this.content = content;
.build(); this.sender = sender;
} }
}
public MailTextContent toProto() {
@Entity return MailTextContent.newBuilder()
public static class MailItem { .setTitle(this.title)
public int itemId; .setContent(this.content)
public int itemCount; .setSender(this.sender)
public int itemLevel; .build();
}
public MailItem() { }
this.itemId = 11101;
this.itemCount = 1; @Entity
this.itemLevel = 1; public static class MailItem {
} public int itemId;
public int itemCount;
public MailItem(int itemId) { public int itemLevel;
this(itemId, 1);
} public MailItem() {
this.itemId = 11101;
public MailItem(int itemId, int itemCount) { this.itemCount = 1;
this(itemId, itemCount, 1); this.itemLevel = 1;
} }
public MailItem(int itemId, int itemCount, int itemLevel) { public MailItem(int itemId) {
this.itemId = itemId; this(itemId, 1);
this.itemCount = itemCount; }
this.itemLevel = itemLevel;
} public MailItem(int itemId, int itemCount) {
this(itemId, itemCount, 1);
public MailItemOuterClass.MailItem toProto() { }
return newBuilder().setEquipParam(EquipParam.newBuilder()
.setItemId(this.itemId) public MailItem(int itemId, int itemCount, int itemLevel) {
.setItemNum(this.itemCount) this.itemId = itemId;
.setItemLevel(this.itemLevel) this.itemCount = itemCount;
.setPromoteLevel(0)//mock this.itemLevel = itemLevel;
.build()) }
.build();
} public MailItemOuterClass.MailItem toProto() {
} return newBuilder()
.setEquipParam(
public void save() { EquipParam.newBuilder()
if (this.expireTime * 1000 < System.currentTimeMillis()) { .setItemId(this.itemId)
DatabaseHelper.deleteMail(this); .setItemNum(this.itemCount)
} else { .setItemLevel(this.itemLevel)
DatabaseHelper.saveMail(this); .setPromoteLevel(0) // mock
} .build())
} .build();
} }
}
public void save() {
if (this.expireTime * 1000 < System.currentTimeMillis()) {
DatabaseHelper.deleteMail(this);
} else {
DatabaseHelper.saveMail(this);
}
}
}

View File

@ -1,133 +1,146 @@
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.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.KillMonsterCountTrigger; 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;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneBossChest; import emu.grasscutter.scripts.data.SceneBossChest;
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 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 final class BlossomActivity {
public final class BlossomActivity {
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 EntityGadget chest;
private EntityGadget chest; private int step;
private int step; private final int goal;
private final int goal; private int generatedCount;
private int generatedCount; private final int worldLevel;
private final int worldLevel; private boolean pass = false;
private boolean pass=false; private final List<EntityMonster> activeMonsters = new ArrayList<>();
private final List<EntityMonster> activeMonsters = new ArrayList<>(); private final Queue<Integer> candidateMonsters = new ArrayDeque<>();
private final Queue<Integer> candidateMonsters = new ArrayDeque<>(); private static final int BLOOMING_GADGET_ID = 70210109;
private static final int BLOOMING_GADGET_ID = 70210109;
public BlossomActivity(EntityGadget entityGadget, List<Integer> monsters, int timeout, int worldLevel) { public BlossomActivity(
this.tempSceneGroup = new SceneGroup(); EntityGadget entityGadget, List<Integer> monsters, int timeout, int worldLevel) {
this.tempSceneGroup.id = entityGadget.getId(); this.tempSceneGroup = new SceneGroup();
this.gadget=entityGadget; this.tempSceneGroup.id = entityGadget.getId();
this.step=0; this.gadget = entityGadget;
this.goal = monsters.size(); this.step = 0;
this.candidateMonsters.addAll(monsters); this.goal = monsters.size();
this.worldLevel = worldLevel; this.candidateMonsters.addAll(monsters);
ArrayList<ChallengeTrigger> challengeTriggers = new ArrayList<>(); this.worldLevel = worldLevel;
this.challenge = new WorldChallenge(entityGadget.getScene(), ArrayList<ChallengeTrigger> challengeTriggers = new ArrayList<>();
tempSceneGroup, this.challenge =
1, new WorldChallenge(
1, entityGadget.getScene(),
List.of(goal, timeout), tempSceneGroup,
timeout, 1,
goal, challengeTriggers); 1,
challengeTriggers.add(new KillMonsterCountTrigger()); List.of(goal, timeout),
//this.challengeTriggers.add(new InTimeTrigger()); timeout,
} goal,
public WorldChallenge getChallenge() { challengeTriggers);
return this.challenge; challengeTriggers.add(new KillMonsterCountTrigger());
} // this.challengeTriggers.add(new InTimeTrigger());
public void setMonsters(List<EntityMonster> monsters) { }
this.activeMonsters.clear();
this.activeMonsters.addAll(monsters); public WorldChallenge getChallenge() {
for (EntityMonster monster : monsters) { return this.challenge;
monster.setGroupId(this.tempSceneGroup.id); }
}
} public void setMonsters(List<EntityMonster> monsters) {
public int getAliveMonstersCount() { this.activeMonsters.clear();
int count=0; this.activeMonsters.addAll(monsters);
for (EntityMonster monster: activeMonsters) { for (EntityMonster monster : monsters) {
if (monster.isAlive()) { monster.setGroupId(this.tempSceneGroup.id);
count++; }
} }
}
return count; public int getAliveMonstersCount() {
} int count = 0;
public boolean getPass() { for (EntityMonster monster : activeMonsters) {
return pass; if (monster.isAlive()) {
} count++;
public void start() { }
challenge.start(); }
} return count;
public void onTick() { }
Scene scene = gadget.getScene();
Position pos = gadget.getPosition(); public boolean getPass() {
if (getAliveMonstersCount() <= 2) { return pass;
if (generatedCount<goal) { }
step++;
public void start() {
var worldLevelData = GameData.getWorldLevelDataMap().get(worldLevel); challenge.start();
int worldLevelOverride = 0; }
if (worldLevelData != null) {
worldLevelOverride = worldLevelData.getMonsterLevel(); public void onTick() {
} Scene scene = gadget.getScene();
Position pos = gadget.getPosition();
List<EntityMonster> newMonsters = new ArrayList<>(); if (getAliveMonstersCount() <= 2) {
int willSpawn = Utils.randomRange(3,5); if (generatedCount < goal) {
if (generatedCount+willSpawn>goal) { step++;
willSpawn = goal - generatedCount;
} var worldLevelData = GameData.getWorldLevelDataMap().get(worldLevel);
generatedCount+=willSpawn; int worldLevelOverride = 0;
for (int i = 0; i < willSpawn; i++) { if (worldLevelData != null) {
var monsterData = GameData.getMonsterDataMap().get(candidateMonsters.poll()); worldLevelOverride = worldLevelData.getMonsterLevel();
int level = scene.getEntityLevel(1, worldLevelOverride); }
EntityMonster entity = new EntityMonster(scene, monsterData, pos.nearby2d(4f), level);
scene.addEntity(entity); List<EntityMonster> newMonsters = new ArrayList<>();
newMonsters.add(entity); int willSpawn = Utils.randomRange(3, 5);
} if (generatedCount + willSpawn > goal) {
setMonsters(newMonsters); willSpawn = goal - generatedCount;
}else { }
if (getAliveMonstersCount() == 0) { generatedCount += willSpawn;
this.pass = true; for (int i = 0; i < willSpawn; i++) {
this.challenge.done(); var monsterData = GameData.getMonsterDataMap().get(candidateMonsters.poll());
} int level = scene.getEntityLevel(1, worldLevelOverride);
} EntityMonster entity = new EntityMonster(scene, monsterData, pos.nearby2d(4f), level);
} scene.addEntity(entity);
} newMonsters.add(entity);
public EntityGadget getGadget() { }
return gadget; setMonsters(newMonsters);
} } else {
public EntityGadget getChest() { if (getAliveMonstersCount() == 0) {
if (chest==null) { this.pass = true;
EntityGadget rewardGadget = new EntityGadget(gadget.getScene(), BLOOMING_GADGET_ID, gadget.getPosition()); this.challenge.done();
SceneGadget metaGadget = new SceneGadget(); }
metaGadget.boss_chest = new SceneBossChest(); }
metaGadget.boss_chest.resin = 20; }
rewardGadget.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, Float.POSITIVE_INFINITY); }
rewardGadget.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, Float.POSITIVE_INFINITY);
rewardGadget.setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, Float.POSITIVE_INFINITY); public EntityGadget getGadget() {
rewardGadget.setMetaGadget(metaGadget); return gadget;
rewardGadget.buildContent(); }
chest = rewardGadget;
} public EntityGadget getChest() {
return chest; if (chest == null) {
} EntityGadget rewardGadget =
} new EntityGadget(gadget.getScene(), BLOOMING_GADGET_ID, gadget.getPosition());
SceneGadget metaGadget = new SceneGadget();
metaGadget.boss_chest = new SceneBossChest();
metaGadget.boss_chest.resin = 20;
rewardGadget.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, Float.POSITIVE_INFINITY);
rewardGadget.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, Float.POSITIVE_INFINITY);
rewardGadget.setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, Float.POSITIVE_INFINITY);
rewardGadget.setMetaGadget(metaGadget);
rewardGadget.buildContent();
chest = rewardGadget;
}
return chest;
}
}

View File

@ -1,418 +1,420 @@
package emu.grasscutter.game.managers.energy; package emu.grasscutter.game.managers.energy;
import static emu.grasscutter.config.Configuration.GAME_OPTIONS; import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.InvalidProtocolBufferException;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.DataLoader; import emu.grasscutter.data.DataLoader;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.ItemData; import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData; import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
import emu.grasscutter.data.excels.monster.MonsterData.HpDrops; import emu.grasscutter.data.excels.monster.MonsterData.HpDrops;
import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.*; import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.player.BasePlayerManager; import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ElementType; import emu.grasscutter.game.props.ElementType;
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 emu.grasscutter.game.props.WeaponType; import emu.grasscutter.game.props.WeaponType;
import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall; import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall;
import emu.grasscutter.net.proto.AbilityIdentifierOuterClass.AbilityIdentifier; import emu.grasscutter.net.proto.AbilityIdentifierOuterClass.AbilityIdentifier;
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry; import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult; import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
import emu.grasscutter.net.proto.ChangeEnergyReasonOuterClass.ChangeEnergyReason; import emu.grasscutter.net.proto.ChangeEnergyReasonOuterClass.ChangeEnergyReason;
import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo; import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo;
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason; import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
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 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.Optional;
import java.util.List; import java.util.concurrent.ThreadLocalRandom;
import java.util.Optional; import lombok.Getter;
import java.util.concurrent.ThreadLocalRandom;
public class EnergyManager extends BasePlayerManager {
public class EnergyManager extends BasePlayerManager { private static final Int2ObjectMap<List<EnergyDropInfo>> energyDropData =
private static final Int2ObjectMap<List<EnergyDropInfo>> energyDropData = new Int2ObjectOpenHashMap<>();
new Int2ObjectOpenHashMap<>(); 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; @Getter 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); this.avatarNormalProbabilities = new Object2IntOpenHashMap<>();
this.avatarNormalProbabilities = new Object2IntOpenHashMap<>(); this.energyUsage = GAME_OPTIONS.energyUsage;
this.energyUsage = GAME_OPTIONS.energyUsage; }
}
public static void initialize() {
public static void initialize() { // Read the data we need for monster energy drops.
// Read the data we need for monster energy drops. try {
try { DataLoader.loadList("EnergyDrop.json", EnergyDropEntry.class)
DataLoader.loadList("EnergyDrop.json", EnergyDropEntry.class) .forEach(
.forEach( entry -> {
entry -> { energyDropData.put(entry.getDropId(), entry.getDropList());
energyDropData.put(entry.getDropId(), entry.getDropList()); });
});
Grasscutter.getLogger().debug("Energy drop data successfully loaded.");
Grasscutter.getLogger().debug("Energy drop data successfully loaded."); } catch (Exception ex) {
} catch (Exception ex) { Grasscutter.getLogger().error("Unable to load energy drop data.", ex);
Grasscutter.getLogger().error("Unable to load energy drop data.", ex); }
}
// Read the data for particle generation from skills
// Read the data for particle generation from skills try {
try { DataLoader.loadList("SkillParticleGeneration.json", SkillParticleGenerationEntry.class)
DataLoader.loadList("SkillParticleGeneration.json", SkillParticleGenerationEntry.class) .forEach(
.forEach( entry -> {
entry -> { skillParticleGenerationData.put(entry.getAvatarId(), entry.getAmountList());
skillParticleGenerationData.put(entry.getAvatarId(), entry.getAmountList()); });
});
Grasscutter.getLogger().debug("Skill particle generation data successfully loaded.");
Grasscutter.getLogger().debug("Skill particle generation data successfully loaded."); } catch (Exception ex) {
} catch (Exception ex) { Grasscutter.getLogger().error("Unable to load skill particle generation data data.", ex);
Grasscutter.getLogger().error("Unable to load skill particle generation data data.", ex); }
} }
}
/** Particle creation for elemental skills. */
/** Particle creation for elemental skills. */ private int getBallCountForAvatar(int avatarId) {
private int getBallCountForAvatar(int avatarId) { // We default to two particles.
// We default to two particles. int count = 2;
int count = 2;
// If we don't have any data for this avatar, stop.
// If we don't have any data for this avatar, stop. if (!skillParticleGenerationData.containsKey(avatarId)) {
if (!skillParticleGenerationData.containsKey(avatarId)) { Grasscutter.getLogger().warn("No particle generation data for avatarId {} found.", avatarId);
Grasscutter.getLogger().warn("No particle generation data for avatarId {} found.", avatarId); }
} // If we do have data, roll for how many particles we should generate.
// If we do have data, roll for how many particles we should generate. else {
else { int roll = ThreadLocalRandom.current().nextInt(0, 100);
int roll = ThreadLocalRandom.current().nextInt(0, 100); int percentageStack = 0;
int percentageStack = 0; for (SkillParticleGenerationInfo info : skillParticleGenerationData.get(avatarId)) {
for (SkillParticleGenerationInfo info : skillParticleGenerationData.get(avatarId)) { int chance = info.getChance();
int chance = info.getChance(); percentageStack += chance;
percentageStack += chance; if (roll < percentageStack) {
if (roll < percentageStack) { count = info.getValue();
count = info.getValue(); break;
break; }
} }
} }
}
// Done.
// Done. return count;
return count; }
}
private int getBallIdForElement(ElementType element) {
private int getBallIdForElement(ElementType element) { // If we have no element, we default to an element-less particle.
// If we have no element, we default to an element-less particle. if (element == null) {
if (element == null) { return 2024;
return 2024; }
}
// Otherwise, we determine the particle's ID based on the element.
// Otherwise, we determine the particle's ID based on the element. return switch (element) {
return switch (element) { case Fire -> 2017;
case Fire -> 2017; case Water -> 2018;
case Water -> 2018; case Grass -> 2019;
case Grass -> 2019; case Electric -> 2020;
case Electric -> 2020; case Wind -> 2021;
case Wind -> 2021; case Ice -> 2022;
case Ice -> 2022; case Rock -> 2023;
case Rock -> 2023; default -> 2024;
default -> 2024; };
}; }
}
public void handleGenerateElemBall(AbilityInvokeEntry invoke)
public void handleGenerateElemBall(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException {
throws InvalidProtocolBufferException { // ToDo:
// ToDo: // This is also called when a weapon like Favonius Warbow etc. creates energy through its
// This is also called when a weapon like Favonius Warbow etc. creates energy through its // passive.
// passive. // We are not handling this correctly at the moment.
// We are not handling this correctly at the moment.
// Get action info.
// Get action info. AbilityActionGenerateElemBall action =
AbilityActionGenerateElemBall action = AbilityActionGenerateElemBall.parseFrom(invoke.getAbilityData());
AbilityActionGenerateElemBall.parseFrom(invoke.getAbilityData()); if (action == null) {
if (action == null) { return;
return; }
}
// Default to an elementless particle.
// Default to an elementless particle. int itemId = 2024;
int itemId = 2024;
// Generate 2 particles by default.
// Generate 2 particles by default. int amount = 2;
int amount = 2;
// Try to get the casting avatar from the player's party.
// Try to get the casting avatar from the player's party. Optional<EntityAvatar> avatarEntity =
Optional<EntityAvatar> avatarEntity = this.getCastingAvatarEntityForEnergy(invoke.getEntityId());
this.getCastingAvatarEntityForEnergy(invoke.getEntityId());
// Bug: invokes twice sometimes, Ayato, Keqing
// Bug: invokes twice sometimes, Ayato, Keqing // ToDo: deal with press, hold difference. deal with charge(Beidou, Yunjin)
// ToDo: deal with press, hold difference. deal with charge(Beidou, Yunjin) if (avatarEntity.isPresent()) {
if (avatarEntity.isPresent()) { Avatar avatar = avatarEntity.get().getAvatar();
Avatar avatar = avatarEntity.get().getAvatar();
if (avatar != null) {
if (avatar != null) { int avatarId = avatar.getAvatarId();
int avatarId = avatar.getAvatarId(); AvatarSkillDepotData skillDepotData = avatar.getSkillDepot();
AvatarSkillDepotData skillDepotData = avatar.getSkillDepot();
// Determine how many particles we need to create for this avatar.
// Determine how many particles we need to create for this avatar. amount = this.getBallCountForAvatar(avatarId);
amount = this.getBallCountForAvatar(avatarId);
// Determine the avatar's element, and based on that the ID of the
// Determine the avatar's element, and based on that the ID of the // particles we have to generate.
// particles we have to generate. if (skillDepotData != null) {
if (skillDepotData != null) { ElementType element = skillDepotData.getElementType();
ElementType element = skillDepotData.getElementType(); itemId = this.getBallIdForElement(element);
itemId = this.getBallIdForElement(element); }
} }
} }
}
// Generate the particles.
// Generate the particles. var pos = new Position(action.getPos());
var pos = new Position(action.getPos()); for (int i = 0; i < amount; i++) {
for (int i = 0; i < amount; i++) { this.generateElemBall(itemId, pos, 1);
this.generateElemBall(itemId, pos, 1); }
} }
}
/**
/** * Energy generation for NAs/CAs.
* Energy generation for NAs/CAs. *
* * @param avatar The avatar.
* @param avatar The avatar. */
*/ private void generateEnergyForNormalAndCharged(EntityAvatar avatar) {
private void generateEnergyForNormalAndCharged(EntityAvatar avatar) { // This logic is based on the descriptions given in
// This logic is based on the descriptions given in // https://genshin-impact.fandom.com/wiki/Energy#Energy_Generated_by_Normal_Attacks
// https://genshin-impact.fandom.com/wiki/Energy#Energy_Generated_by_Normal_Attacks // https://library.keqingmains.com/combat-mechanics/energy#auto-attacking
// https://library.keqingmains.com/combat-mechanics/energy#auto-attacking // Those descriptions are lacking in some information, so this implementation most likely
// Those descriptions are lacking in some information, so this implementation most likely // does not fully replicate the behavior of the official server. Open questions:
// does not fully replicate the behavior of the official server. Open questions: // - Does the probability for a character reset after some time?
// - Does the probability for a character reset after some time? // - Does the probability for a character reset when switching them out?
// - Does the probability for a character reset when switching them out? // - Does this really count every individual hit separately?
// - Does this really count every individual hit separately?
// Get the avatar's weapon type.
// Get the avatar's weapon type. WeaponType weaponType = avatar.getAvatar().getAvatarData().getWeaponType();
WeaponType weaponType = avatar.getAvatar().getAvatarData().getWeaponType();
// Check if we already have probability data for this avatar. If not, insert it.
// Check if we already have probability data for this avatar. If not, insert it. if (!this.avatarNormalProbabilities.containsKey(avatar)) {
if (!this.avatarNormalProbabilities.containsKey(avatar)) { this.avatarNormalProbabilities.put(avatar, weaponType.getEnergyGainInitialProbability());
this.avatarNormalProbabilities.put(avatar, weaponType.getEnergyGainInitialProbability()); }
}
// Roll for energy.
// Roll for energy. int currentProbability = this.avatarNormalProbabilities.getInt(avatar);
int currentProbability = this.avatarNormalProbabilities.getInt(avatar); int roll = ThreadLocalRandom.current().nextInt(0, 100);
int roll = ThreadLocalRandom.current().nextInt(0, 100);
// If the player wins the roll, we increase the avatar's energy and reset the probability.
// If the player wins the roll, we increase the avatar's energy and reset the probability. if (roll < currentProbability) {
if (roll < currentProbability) { avatar.addEnergy(1.0f, PropChangeReason.PROP_CHANGE_REASON_ABILITY, true);
avatar.addEnergy(1.0f, PropChangeReason.PROP_CHANGE_REASON_ABILITY, true); this.avatarNormalProbabilities.put(avatar, weaponType.getEnergyGainInitialProbability());
this.avatarNormalProbabilities.put(avatar, weaponType.getEnergyGainInitialProbability()); }
} // Otherwise, we increase the probability for the next hit.
// Otherwise, we increase the probability for the next hit. else {
else { this.avatarNormalProbabilities.put(
this.avatarNormalProbabilities.put( avatar, currentProbability + weaponType.getEnergyGainIncreaseProbability());
avatar, currentProbability + weaponType.getEnergyGainIncreaseProbability()); }
} }
}
public void handleAttackHit(EvtBeingHitInfo hitInfo) {
public void handleAttackHit(EvtBeingHitInfo hitInfo) { // Get the attack result.
// Get the attack result. AttackResult attackRes = hitInfo.getAttackResult();
AttackResult attackRes = hitInfo.getAttackResult();
// Make sure the attack was performed by the currently active avatar. If not, we ignore the hit.
// Make sure the attack was performed by the currently active avatar. If not, we ignore the hit. Optional<EntityAvatar> attackerEntity =
Optional<EntityAvatar> attackerEntity = this.getCastingAvatarEntityForEnergy(attackRes.getAttackerId());
this.getCastingAvatarEntityForEnergy(attackRes.getAttackerId()); if (attackerEntity.isEmpty()
if (attackerEntity.isEmpty() || this.player.getTeamManager().getCurrentAvatarEntity().getId()
|| this.player.getTeamManager().getCurrentAvatarEntity().getId() != attackerEntity.get().getId()) {
!= attackerEntity.get().getId()) { return;
return; }
}
// Make sure the target is an actual enemy.
// Make sure the target is an actual enemy. GameEntity targetEntity = this.player.getScene().getEntityById(attackRes.getDefenseId());
GameEntity targetEntity = this.player.getScene().getEntityById(attackRes.getDefenseId()); if (!(targetEntity instanceof EntityMonster targetMonster)) {
if (!(targetEntity instanceof EntityMonster targetMonster)) { return;
return; }
}
MonsterType targetType = targetMonster.getMonsterData().getType();
MonsterType targetType = targetMonster.getMonsterData().getType(); if (targetType != MonsterType.MONSTER_ORDINARY && targetType != MonsterType.MONSTER_BOSS) {
if (targetType != MonsterType.MONSTER_ORDINARY && targetType != MonsterType.MONSTER_BOSS) { return;
return; }
}
// Get the ability that caused this hit.
// Get the ability that caused this hit. AbilityIdentifier ability = attackRes.getAbilityIdentifier();
AbilityIdentifier ability = attackRes.getAbilityIdentifier();
// Make sure there is no actual "ability" associated with the hit. For now, this is how we
// Make sure there is no actual "ability" associated with the hit. For now, this is how we // identify normal and charged attacks. Note that this is not completely accurate:
// identify normal and charged attacks. Note that this is not completely accurate: // - Many character's charged attacks have an ability associated with them. This means that,
// - Many character's charged attacks have an ability associated with them. This means that, // for now, we don't identify charged attacks reliably.
// for now, we don't identify charged attacks reliably. // - There might also be some cases where we incorrectly identify something as a normal or
// - There might also be some cases where we incorrectly identify something as a normal or // charged attack that is not (Diluc's E?).
// charged attack that is not (Diluc's E?). // - Catalyst normal attacks have an ability, so we don't handle those for now.
// - Catalyst normal attacks have an ability, so we don't handle those for now. // ToDo: Fix all of that.
// ToDo: Fix all of that. if (ability != AbilityIdentifier.getDefaultInstance()) {
if (ability != AbilityIdentifier.getDefaultInstance()) { return;
return; }
}
// Handle the energy generation.
// Handle the energy generation. this.generateEnergyForNormalAndCharged(attackerEntity.get());
this.generateEnergyForNormalAndCharged(attackerEntity.get()); }
}
/*
/* * Energy logic related to using skills.
* Energy logic related to using skills. */
*/
private void handleBurstCast(Avatar avatar, int skillId) {
private void handleBurstCast(Avatar avatar, int skillId) { // Don't do anything if energy usage is disabled.
// Don't do anything if energy usage is disabled. if (!GAME_OPTIONS.energyUsage || !this.energyUsage) {
if (!GAME_OPTIONS.energyUsage || !this.energyUsage) { return;
return; }
}
// If the cast skill was a burst, consume energy.
// If the cast skill was a burst, consume energy. if (avatar.getSkillDepot() != null && skillId == avatar.getSkillDepot().getEnergySkill()) {
if (avatar.getSkillDepot() != null && skillId == avatar.getSkillDepot().getEnergySkill()) { avatar.getAsEntity().clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START);
avatar.getAsEntity().clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START); }
} }
}
public void handleEvtDoSkillSuccNotify(GameSession session, int skillId, int casterId) {
public void handleEvtDoSkillSuccNotify(GameSession session, int skillId, int casterId) { // Determine the entity that has cast the skill. Cancel if we can't find that avatar.
// Determine the entity that has cast the skill. Cancel if we can't find that avatar. Optional<EntityAvatar> caster =
Optional<EntityAvatar> caster = this.player.getTeamManager().getActiveTeam().stream()
this.player.getTeamManager().getActiveTeam().stream() .filter(character -> character.getId() == casterId)
.filter(character -> character.getId() == casterId) .findFirst();
.findFirst();
if (caster.isEmpty()) {
if (caster.isEmpty()) { return;
return; }
}
Avatar avatar = caster.get().getAvatar();
Avatar avatar = caster.get().getAvatar();
// Handle elemental burst.
// Handle elemental burst. this.handleBurstCast(avatar, skillId);
this.handleBurstCast(avatar, skillId); }
}
/*
/* * Monster energy drops.
* Monster energy drops. */
*/
private void generateElemBallDrops(EntityMonster monster, int dropId) {
private void generateElemBallDrops(EntityMonster monster, int dropId) { // Generate all drops specified for the given drop id.
// Generate all drops specified for the given drop id. if (!energyDropData.containsKey(dropId)) {
if (!energyDropData.containsKey(dropId)) { Grasscutter.getLogger().warn("No drop data for dropId {} found.", dropId);
Grasscutter.getLogger().warn("No drop data for dropId {} found.", dropId); return;
return; }
}
for (EnergyDropInfo info : energyDropData.get(dropId)) {
for (EnergyDropInfo info : energyDropData.get(dropId)) { this.generateElemBall(info.getBallId(), monster.getPosition(), info.getCount());
this.generateElemBall(info.getBallId(), monster.getPosition(), info.getCount()); }
} }
}
public void handleMonsterEnergyDrop(
public void handleMonsterEnergyDrop( EntityMonster monster, float hpBeforeDamage, float hpAfterDamage) {
EntityMonster monster, float hpBeforeDamage, float hpAfterDamage) { // Make sure this is actually a monster.
// Make sure this is actually a monster. // Note that some wildlife also has that type, like boars or birds.
// Note that some wildlife also has that type, like boars or birds. MonsterType type = monster.getMonsterData().getType();
MonsterType type = monster.getMonsterData().getType(); if (type != MonsterType.MONSTER_ORDINARY && type != MonsterType.MONSTER_BOSS) {
if (type != MonsterType.MONSTER_ORDINARY && type != MonsterType.MONSTER_BOSS) { return;
return; }
}
// Calculate the HP thresholds for before and after the damage was taken.
// Calculate the HP thresholds for before and after the damage was taken. float maxHp = monster.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
float maxHp = monster.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); float thresholdBefore = hpBeforeDamage / maxHp;
float thresholdBefore = hpBeforeDamage / maxHp; float thresholdAfter = hpAfterDamage / maxHp;
float thresholdAfter = hpAfterDamage / maxHp;
// Determine the thresholds the monster has passed, and generate drops based on that.
// Determine the thresholds the monster has passed, and generate drops based on that. for (HpDrops drop : monster.getMonsterData().getHpDrops()) {
for (HpDrops drop : monster.getMonsterData().getHpDrops()) { if (drop.getDropId() == 0) {
if (drop.getDropId() == 0) { continue;
continue; }
}
float threshold = drop.getHpPercent() / 100.0f;
float threshold = drop.getHpPercent() / 100.0f; if (threshold < thresholdBefore && threshold >= thresholdAfter) {
if (threshold < thresholdBefore && threshold >= thresholdAfter) { this.generateElemBallDrops(monster, drop.getDropId());
this.generateElemBallDrops(monster, drop.getDropId()); }
} }
}
// Handle kill drops.
// Handle kill drops. if (hpAfterDamage <= 0 && monster.getMonsterData().getKillDropId() != 0) {
if (hpAfterDamage <= 0 && monster.getMonsterData().getKillDropId() != 0) { this.generateElemBallDrops(monster, monster.getMonsterData().getKillDropId());
this.generateElemBallDrops(monster, monster.getMonsterData().getKillDropId()); }
} }
}
/*
/* * Utilities.
* Utilities. */
*/
private void generateElemBall(int ballId, Position position, int count) {
private void generateElemBall(int ballId, Position position, int count) { // Generate a particle/orb with the specified parameters.
// Generate a particle/orb with the specified parameters. ItemData itemData = GameData.getItemDataMap().get(ballId);
ItemData itemData = GameData.getItemDataMap().get(ballId); if (itemData == null) {
if (itemData == null) { return;
return; }
}
EntityItem energyBall =
EntityItem energyBall = new EntityItem(this.getPlayer().getScene(), this.getPlayer(), itemData, position, count);
new EntityItem(this.getPlayer().getScene(), this.getPlayer(), itemData, position, count); this.getPlayer().getScene().addEntity(energyBall);
this.getPlayer().getScene().addEntity(energyBall); }
}
private Optional<EntityAvatar> getCastingAvatarEntityForEnergy(int invokeEntityId) {
private Optional<EntityAvatar> getCastingAvatarEntityForEnergy(int invokeEntityId) { // To determine the avatar that has cast the skill that caused the energy particle to be
// To determine the avatar that has cast the skill that caused the energy particle to be // generated,
// generated, // we have to look at the entity that has invoked the ability. This can either be that avatar
// we have to look at the entity that has invoked the ability. This can either be that avatar // directly,
// directly, // or it can be an `EntityClientGadget`, owned (some way up the owner hierarchy) by the avatar
// or it can be an `EntityClientGadget`, owned (some way up the owner hierarchy) by the avatar // that cast the skill.
// that cast the skill.
// Try to get the invoking entity from the scene.
// Try to get the invoking entity from the scene. GameEntity entity = this.player.getScene().getEntityById(invokeEntityId);
GameEntity entity = this.player.getScene().getEntityById(invokeEntityId);
// Determine the ID of the entity that originally cast this skill. If the scene entity is null,
// Determine the ID of the entity that originally cast this skill. If the scene entity is null, // or not an `EntityClientGadget`, we assume that we are directly looking at the casting avatar
// or not an `EntityClientGadget`, we assume that we are directly looking at the casting avatar // (the null case will happen if the avatar was switched out between casting the skill and the
// (the null case will happen if the avatar was switched out between casting the skill and the // particle being generated). If the scene entity is an `EntityClientGadget`, we need to find
// particle being generated). If the scene entity is an `EntityClientGadget`, we need to find // the
// the // ID of the original owner of that gadget.
// ID of the original owner of that gadget. int avatarEntityId =
int avatarEntityId = (!(entity instanceof EntityClientGadget))
(!(entity instanceof EntityClientGadget)) ? invokeEntityId
? invokeEntityId : ((EntityClientGadget) entity).getOriginalOwnerEntityId();
: ((EntityClientGadget) entity).getOriginalOwnerEntityId();
// Finally, find the avatar entity in the player's team.
// Finally, find the avatar entity in the player's team. return this.player.getTeamManager().getActiveTeam().stream()
return this.player.getTeamManager().getActiveTeam().stream() .filter(character -> character.getId() == avatarEntityId)
.filter(character -> character.getId() == avatarEntityId) .findFirst();
.findFirst(); }
}
/**
/** * Refills the energy of the active avatar.
* Refills the energy of the active avatar. *
* * @return True if the energy was refilled, false otherwise.
* @return True if the energy was refilled, false otherwise. */
*/ public boolean refillActiveEnergy() {
public boolean refillActiveEnergy() { var activeEntity = this.player.getTeamManager().getCurrentAvatarEntity();
var activeEntity = this.player.getTeamManager().getCurrentAvatarEntity(); return activeEntity.addEnergy(
return activeEntity.addEnergy(activeEntity.getAvatar().getSkillDepot().getEnergySkillData().getCostElemVal()); activeEntity.getAvatar().getSkillDepot().getEnergySkillData().getCostElemVal());
} }
/** /**
* Refills the energy of the entire team. * Refills the energy of the entire team.
* *
* @param changeReason The reason for the energy change. * @param changeReason The reason for the energy change.
* @param isFlat Whether the energy should be added as a flat value. * @param isFlat Whether the energy should be added as a flat value.
*/ */
public void refillTeamEnergy(PropChangeReason changeReason, boolean isFlat) { public void refillTeamEnergy(PropChangeReason changeReason, boolean isFlat) {
for (var entityAvatar : this.player.getTeamManager().getActiveTeam()) { for (var entityAvatar : this.player.getTeamManager().getActiveTeam()) {
// giving the exact amount read off the AvatarSkillData.json // giving the exact amount read off the AvatarSkillData.json
entityAvatar.addEnergy(entityAvatar.getAvatar().getSkillDepot() entityAvatar.addEnergy(
.getEnergySkillData().getCostElemVal(), changeReason, isFlat); entityAvatar.getAvatar().getSkillDepot().getEnergySkillData().getCostElemVal(),
} changeReason,
} isFlat);
}
public void setEnergyUsage(boolean energyUsage) { }
this.energyUsage = energyUsage;
if (!energyUsage) { // Refill team energy if usage is disabled public void setEnergyUsage(boolean energyUsage) {
for (EntityAvatar entityAvatar : this.player.getTeamManager().getActiveTeam()) { this.energyUsage = energyUsage;
entityAvatar.addEnergy(1000, PropChangeReason.PROP_CHANGE_REASON_GM, true); if (!energyUsage) { // Refill team energy if usage is disabled
} for (EntityAvatar entityAvatar : this.player.getTeamManager().getActiveTeam()) {
} entityAvatar.addEnergy(1000, PropChangeReason.PROP_CHANGE_REASON_GM, true);
} }
} }
}
}

View File

@ -1,280 +1,276 @@
package emu.grasscutter.game.player; package emu.grasscutter.game.player;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.ScenePointEntry; 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.QuestCond;
import emu.grasscutter.game.quest.enums.QuestContent; 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.stream.Collectors;
import java.util.Set;
import java.util.stream.Collectors; // @Entity
public final class PlayerProgressManager extends BasePlayerDataManager {
// @Entity /******************************************************************************************************************
public final class PlayerProgressManager extends BasePlayerDataManager { ******************************************************************************************************************
/****************************************************************************************************************** * OPEN STATES
****************************************************************************************************************** ******************************************************************************************************************
* OPEN STATES *****************************************************************************************************************/
******************************************************************************************************************
*****************************************************************************************************************/ // Set of open states that are never unlocked, whether they fulfill the conditions or not.
public static final Set<Integer> BLACKLIST_OPEN_STATES =
// Set of open states that are never unlocked, whether they fulfill the conditions or not. Set.of(
public static final Set<Integer> BLACKLIST_OPEN_STATES = 48 // blacklist OPEN_STATE_LIMIT_REGION_GLOBAL to make Meledy happy. =D Remove this as
Set.of( // soon as quest unlocks are fully implemented.
48 // blacklist OPEN_STATE_LIMIT_REGION_GLOBAL to make Meledy happy. =D Remove this as );
// soon as quest unlocks are fully implemented. // Set of open states that are set per default for all accounts. Can be overwritten by an entry in
); // `map`.
// Set of open states that are set per default for all accounts. Can be overwritten by an entry in public static final Set<Integer> DEFAULT_OPEN_STATES =
// `map`. GameData.getOpenStateList().stream()
public static final Set<Integer> DEFAULT_OPEN_STATES = .filter(
GameData.getOpenStateList().stream() s ->
.filter( s.isDefaultState() // Actual default-opened states.
s -> // All states whose unlock we don't handle correctly yet.
s.isDefaultState() // Actual default-opened states. || (s.getCond().stream()
// All states whose unlock we don't handle correctly yet. .filter(
|| (s.getCond().stream() c ->
.filter( c.getCondType()
c -> == OpenStateCondType.OPEN_STATE_COND_PLAYER_LEVEL)
c.getCondType() .count()
== OpenStateCondType.OPEN_STATE_COND_PLAYER_LEVEL) == 0)
.count() // Always unlock OPEN_STATE_PAIMON, otherwise the player will not have a
== 0) // working chat.
// Always unlock OPEN_STATE_PAIMON, otherwise the player will not have a || s.getId() == 1)
// working chat. .filter(
|| s.getId() == 1) s ->
.filter( !BLACKLIST_OPEN_STATES.contains(s.getId())) // Filter out states in the blacklist.
s -> .map(s -> s.getId())
!BLACKLIST_OPEN_STATES.contains(s.getId())) // Filter out states in the blacklist. .collect(Collectors.toSet());
.map(s -> s.getId())
.collect(Collectors.toSet()); public PlayerProgressManager(Player player) {
super(player);
public PlayerProgressManager(Player player) { }
super(player);
} /**********
* Handler for player login.
/********** **********/
* Handler for player login. public void onPlayerLogin() {
**********/ // Try unlocking open states on player login. This handles accounts where unlock conditions were
public void onPlayerLogin() { // already met before certain open state unlocks were implemented.
// Try unlocking open states on player login. This handles accounts where unlock conditions were this.tryUnlockOpenStates(false);
// already met before certain open state unlocks were implemented.
this.tryUnlockOpenStates(false); // Send notify to the client.
player.getSession().send(new PacketOpenStateUpdateNotify(this.player));
// Send notify to the client.
player.getSession().send(new PacketOpenStateUpdateNotify(this.player)); // Add statue quests if necessary.
this.addStatueQuestsOnLogin();
// Add statue quests if necessary.
this.addStatueQuestsOnLogin(); // Auto-unlock the first statue and map area, until we figure out how to make
// that particular statue interactable.
// Auto-unlock the first statue and map area, until we figure out how to make this.player.getUnlockedScenePoints(3).add(7);
// that particular statue interactable. this.player.getUnlockedSceneAreas(3).add(1);
this.player.getUnlockedScenePoints(3).add(7); }
this.player.getUnlockedSceneAreas(3).add(1);
} /**********
* Direct getters and setters for open states.
/********** **********/
* Direct getters and setters for open states. public int getOpenState(int openState) {
**********/ return this.player.getOpenStates().getOrDefault(openState, 0);
public int getOpenState(int openState) { }
return this.player.getOpenStates().getOrDefault(openState, 0);
} private void setOpenState(int openState, int value, boolean sendNotify) {
int previousValue = this.player.getOpenStates().getOrDefault(openState, 0);
private void setOpenState(int openState, int value, boolean sendNotify) {
int previousValue = this.player.getOpenStates().getOrDefault(openState, 0); if (value != previousValue) {
this.player.getOpenStates().put(openState, value);
if (value != previousValue) {
this.player.getOpenStates().put(openState, value); if (sendNotify) {
player.getSession().send(new PacketOpenStateChangeNotify(openState, value));
if (sendNotify) { }
player.getSession().send(new PacketOpenStateChangeNotify(openState, value)); }
} }
}
} private void setOpenState(int openState, int value) {
this.setOpenState(openState, value, true);
private void setOpenState(int openState, int value) { }
this.setOpenState(openState, value, true);
} /**********
* Condition checking for setting open states.
/********** **********/
* Condition checking for setting open states. private boolean areConditionsMet(OpenStateData openState) {
**********/ // Check all conditions and test if at least one of them is violated.
private boolean areConditionsMet(OpenStateData openState) { for (var condition : openState.getCond()) {
// Check all conditions and test if at least one of them is violated. // For level conditions, check if the player has reached the necessary level.
for (var condition : openState.getCond()) { if (condition.getCondType() == OpenStateCondType.OPEN_STATE_COND_PLAYER_LEVEL) {
// For level conditions, check if the player has reached the necessary level. if (this.player.getLevel() < condition.getParam()) {
if (condition.getCondType() == OpenStateCondType.OPEN_STATE_COND_PLAYER_LEVEL) { return false;
if (this.player.getLevel() < condition.getParam()) { }
return false; } else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_COND_QUEST) {
} // ToDo: Implement.
} else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_COND_QUEST) { } else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_COND_PARENT_QUEST) {
// ToDo: Implement. // ToDo: Implement.
} else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_COND_PARENT_QUEST) { } else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_OFFERING_LEVEL) {
// ToDo: Implement. // ToDo: Implement.
} else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_OFFERING_LEVEL) { } else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_CITY_REPUTATION_LEVEL) {
// ToDo: Implement. // ToDo: Implement.
} else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_CITY_REPUTATION_LEVEL) { }
// ToDo: Implement. }
}
} // Done. If we didn't find any violations, all conditions are met.
return true;
// Done. If we didn't find any violations, all conditions are met. }
return true;
} /**********
* Setting open states from the client (via `SetOpenStateReq`).
/********** **********/
* Setting open states from the client (via `SetOpenStateReq`). public void setOpenStateFromClient(int openState, int value) {
**********/ // Get the data for this open state.
public void setOpenStateFromClient(int openState, int value) { OpenStateData data = GameData.getOpenStateDataMap().get(openState);
// Get the data for this open state. if (data == null) {
OpenStateData data = GameData.getOpenStateDataMap().get(openState); this.player.sendPacket(new PacketSetOpenStateRsp(Retcode.RET_FAIL));
if (data == null) { return;
this.player.sendPacket(new PacketSetOpenStateRsp(Retcode.RET_FAIL)); }
return;
} // Make sure that this is an open state that the client is allowed to set,
// and that it doesn't have any further conditions attached.
// Make sure that this is an open state that the client is allowed to set, if (!data.isAllowClientOpen() || !this.areConditionsMet(data)) {
// and that it doesn't have any further conditions attached. this.player.sendPacket(new PacketSetOpenStateRsp(Retcode.RET_FAIL));
if (!data.isAllowClientOpen() || !this.areConditionsMet(data)) { return;
this.player.sendPacket(new PacketSetOpenStateRsp(Retcode.RET_FAIL)); }
return;
} // Set.
this.setOpenState(openState, value);
// Set. this.player.sendPacket(new PacketSetOpenStateRsp(openState, value));
this.setOpenState(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);
* 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.)
} **********/
public void tryUnlockOpenStates(boolean sendNotify) {
/********** // Get list of open states that are not yet unlocked.
* Triggered unlocking of open states (unlock states whose conditions have been met.) var lockedStates =
**********/ GameData.getOpenStateList().stream()
public void tryUnlockOpenStates(boolean sendNotify) { .filter(s -> this.player.getOpenStates().getOrDefault(s, 0) == 0)
// Get list of open states that are not yet unlocked. .toList();
var lockedStates =
GameData.getOpenStateList().stream() // Try unlocking all of them.
.filter(s -> this.player.getOpenStates().getOrDefault(s, 0) == 0) for (var state : lockedStates) {
.toList(); // To auto-unlock a state, it has to meet three conditions:
// * it can not be a state that is unlocked by the client,
// Try unlocking all of them. // * it has to meet all its unlock conditions, and
for (var state : lockedStates) { // * it can not be in the blacklist.
// To auto-unlock a state, it has to meet three conditions: if (!state.isAllowClientOpen()
// * it can not be a state that is unlocked by the client, && this.areConditionsMet(state)
// * it has to meet all its unlock conditions, and && !BLACKLIST_OPEN_STATES.contains(state.getId())) {
// * it can not be in the blacklist. this.setOpenState(state.getId(), 1, sendNotify);
if (!state.isAllowClientOpen() }
&& this.areConditionsMet(state) }
&& !BLACKLIST_OPEN_STATES.contains(state.getId())) { }
this.setOpenState(state.getId(), 1, sendNotify);
} public void tryUnlockOpenStates() {
} this.tryUnlockOpenStates(true);
} }
public void tryUnlockOpenStates() { /******************************************************************************************************************
this.tryUnlockOpenStates(true); ******************************************************************************************************************
} * MAP AREAS AND POINTS
******************************************************************************************************************
/****************************************************************************************************************** *****************************************************************************************************************/
****************************************************************************************************************** private void addStatueQuestsOnLogin() {
* MAP AREAS AND POINTS // Get all currently existing subquests for the "unlock all statues" main quest.
****************************************************************************************************************** var statueMainQuest = GameData.getMainQuestDataMap().get(303);
*****************************************************************************************************************/ var statueSubQuests = statueMainQuest.getSubQuests();
private void addStatueQuestsOnLogin() {
// Get all currently existing subquests for the "unlock all statues" main quest. // Add the main statue quest if it isn't active yet.
var statueMainQuest = GameData.getMainQuestDataMap().get(303); var statueGameMainQuest = this.player.getQuestManager().getMainQuestById(303);
var statueSubQuests = statueMainQuest.getSubQuests(); if (statueGameMainQuest == null) {
this.player.getQuestManager().addQuest(30302);
// Add the main statue quest if it isn't active yet. statueGameMainQuest = this.player.getQuestManager().getMainQuestById(303);
var statueGameMainQuest = this.player.getQuestManager().getMainQuestById(303); }
if (statueGameMainQuest == null) {
this.player.getQuestManager().addQuest(30302); // Set all subquests to active if they aren't already finished.
statueGameMainQuest = this.player.getQuestManager().getMainQuestById(303); for (var subData : statueSubQuests) {
} var subGameQuest = statueGameMainQuest.getChildQuestById(subData.getSubId());
if (subGameQuest != null && subGameQuest.getState() == QuestState.QUEST_STATE_UNSTARTED) {
// Set all subquests to active if they aren't already finished. this.player.getQuestManager().addQuest(subData.getSubId());
for (var subData : statueSubQuests) { }
var subGameQuest = statueGameMainQuest.getChildQuestById(subData.getSubId()); }
if (subGameQuest != null && subGameQuest.getState() == QuestState.QUEST_STATE_UNSTARTED) { }
this.player.getQuestManager().addQuest(subData.getSubId());
} public boolean unlockTransPoint(int sceneId, int pointId, boolean isStatue) {
} // Check whether the unlocked point exists and whether it is still locked.
} ScenePointEntry scenePointEntry = GameData.getScenePointEntryById(sceneId, pointId);
public boolean unlockTransPoint(int sceneId, int pointId, boolean isStatue) { if (scenePointEntry == null || this.player.getUnlockedScenePoints(sceneId).contains(pointId)) {
// Check whether the unlocked point exists and whether it is still locked. return false;
ScenePointEntry scenePointEntry = GameData.getScenePointEntryById(sceneId, pointId); }
if (scenePointEntry == null || this.player.getUnlockedScenePoints(sceneId).contains(pointId)) { // Add the point to the list of unlocked points for its scene.
return false; this.player.getUnlockedScenePoints(sceneId).add(pointId);
}
// Give primogems and Adventure EXP for unlocking.
// Add the point to the list of unlocked points for its scene. this.player.getInventory().addItem(201, 5, ActionReason.UnlockPointReward);
this.player.getUnlockedScenePoints(sceneId).add(pointId); this.player.getInventory().addItem(102, isStatue ? 50 : 10, ActionReason.UnlockPointReward);
// Give primogems and Adventure EXP for unlocking. // this.player.sendPacket(new
this.player.getInventory().addItem(201, 5, ActionReason.UnlockPointReward); // PacketPlayerPropChangeReasonNotify(this.player.getProperty(PlayerProperty.PROP_PLAYER_EXP),
this.player.getInventory().addItem(102, isStatue ? 50 : 10, ActionReason.UnlockPointReward); // PlayerProperty.PROP_PLAYER_EXP, PropChangeReason.PROP_CHANGE_REASON_PLAYER_ADD_EXP));
// this.player.sendPacket(new // Fire quest trigger for trans point unlock.
// PacketPlayerPropChangeReasonNotify(this.player.getProperty(PlayerProperty.PROP_PLAYER_EXP), this.player
// PlayerProperty.PROP_PLAYER_EXP, PropChangeReason.PROP_CHANGE_REASON_PLAYER_ADD_EXP)); .getQuestManager()
.queueEvent(QuestContent.QUEST_CONTENT_UNLOCK_TRANS_POINT, sceneId, pointId);
// Fire quest trigger for trans point unlock.
this.player // Send packet.
.getQuestManager() this.player.sendPacket(new PacketScenePointUnlockNotify(sceneId, pointId));
.queueEvent(QuestContent.QUEST_CONTENT_UNLOCK_TRANS_POINT, sceneId, pointId); return true;
}
// Send packet.
this.player.sendPacket(new PacketScenePointUnlockNotify(sceneId, pointId)); public void unlockSceneArea(int sceneId, int areaId) {
return true; // Add the area to the list of unlocked areas in its scene.
} this.player.getUnlockedSceneAreas(sceneId).add(areaId);
public void unlockSceneArea(int sceneId, int areaId) { // Send packet.
// Add the area to the list of unlocked areas in its scene. this.player.sendPacket(new PacketSceneAreaUnlockNotify(sceneId, areaId));
this.player.getUnlockedSceneAreas(sceneId).add(areaId); }
// Send packet. /** Give replace costume to player (Amber, Jean, Mona, Rosaria) */
this.player.sendPacket(new PacketSceneAreaUnlockNotify(sceneId, areaId)); public void addReplaceCostumes() {
} var currentPlayerCostumes = player.getCostumeList();
GameData.getAvatarReplaceCostumeDataMap()
/** .keySet()
* Give replace costume to player (Amber, Jean, Mona, Rosaria) .forEach(
*/ costumeId -> {
public void addReplaceCostumes(){ if (GameData.getAvatarCostumeDataMap().get(costumeId) == null
var currentPlayerCostumes = player.getCostumeList(); || currentPlayerCostumes.contains(costumeId)) {
GameData.getAvatarReplaceCostumeDataMap().keySet().forEach(costumeId -> { return;
if (GameData.getAvatarCostumeDataMap().get(costumeId) == null || currentPlayerCostumes.contains(costumeId)){ }
return; this.player.addCostume(costumeId);
} });
this.player.addCostume(costumeId); }
});
} /** Quest progress */
public void addQuestProgress(int id, int count) {
/** var newCount = player.getPlayerProgress().addToCurrentProgress(id, count);
* Quest progress player.save();
*/ player
public void addQuestProgress(int id, int count){ .getQuestManager()
var newCount = player.getPlayerProgress().addToCurrentProgress(id, count); .queueEvent(QuestContent.QUEST_CONTENT_ADD_QUEST_PROGRESS, id, newCount);
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);
* Item history player.save();
*/ player.getQuestManager().queueEvent(QuestCond.QUEST_COND_HISTORY_GOT_ANY_ITEM, id, newCount);
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);
}
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,23 +1,21 @@
package emu.grasscutter.game.quest.conditions; package emu.grasscutter.game.quest.conditions;
import emu.grasscutter.data.excels.QuestData; import emu.grasscutter.data.excels.QuestData;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.QuestValueCond; import emu.grasscutter.game.quest.QuestValueCond;
import emu.grasscutter.game.quest.enums.QuestCond; import emu.grasscutter.game.quest.enums.QuestCond;
import lombok.val;
@QuestValueCond(QuestCond.QUEST_COND_PERSONAL_LINE_UNLOCK)
@QuestValueCond(QuestCond.QUEST_COND_PERSONAL_LINE_UNLOCK) public class ConditionPersonalLineUnlock extends BaseCondition {
public class ConditionPersonalLineUnlock extends BaseCondition {
@Override
@Override public boolean execute(
public boolean execute( Player owner,
Player owner, QuestData questData,
QuestData questData, QuestData.QuestAcceptCondition condition,
QuestData.QuestAcceptCondition condition, String paramStr,
String paramStr, int... params) {
int... params var personalLineId = condition.getParam()[0];
) { return owner.getPersonalLineList().contains(personalLineId);
var personalLineId = condition.getParam()[0]; }
return owner.getPersonalLineList().contains(personalLineId); }
}
}

View File

@ -1,17 +1,17 @@
package emu.grasscutter.game.quest.exec; package emu.grasscutter.game.quest.exec;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.excels.QuestData; import emu.grasscutter.data.excels.QuestData;
import emu.grasscutter.game.quest.GameQuest; import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueExec; 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;
@QuestValueExec(QuestExec.QUEST_EXEC_ADD_CUR_AVATAR_ENERGY) @QuestValueExec(QuestExec.QUEST_EXEC_ADD_CUR_AVATAR_ENERGY)
public class ExecAddCurAvatarEnergy extends QuestExecHandler { 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().refillActiveEnergy(); return quest.getOwner().getEnergyManager().refillActiveEnergy();
} }
} }

View File

@ -1,21 +1,21 @@
package emu.grasscutter.game.quest.exec; package emu.grasscutter.game.quest.exec;
import emu.grasscutter.data.excels.QuestData; import emu.grasscutter.data.excels.QuestData;
import emu.grasscutter.game.quest.GameQuest; import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueExec; 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;
@QuestValueExec(QuestExec.QUEST_EXEC_ADD_QUEST_PROGRESS) @QuestValueExec(QuestExec.QUEST_EXEC_ADD_QUEST_PROGRESS)
public final 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 =
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().addQuestProgress(param[0], param[1]); quest.getOwner().getProgressManager().addQuestProgress(param[0], param[1]);
return true; return true;
} }
} }

View File

@ -1,20 +1,20 @@
package emu.grasscutter.game.quest.exec; package emu.grasscutter.game.quest.exec;
import emu.grasscutter.data.excels.QuestData; import emu.grasscutter.data.excels.QuestData;
import emu.grasscutter.game.quest.GameQuest; import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueExec; 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;
@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) {
var 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]);
return true; return true;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,86 +1,85 @@
package emu.grasscutter.game.world; package emu.grasscutter.game.world;
import java.util.HashSet; import dev.morphia.annotations.Entity;
import java.util.Map; import dev.morphia.annotations.Id;
import java.util.Set; import dev.morphia.annotations.Indexed;
import java.util.concurrent.ConcurrentHashMap; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.player.Player;
import org.bson.types.ObjectId; import emu.grasscutter.scripts.data.SceneGadget;
import emu.grasscutter.scripts.data.SceneGroup;
import dev.morphia.annotations.Entity; import java.util.HashSet;
import dev.morphia.annotations.Id; import java.util.Map;
import dev.morphia.annotations.Indexed; import java.util.Set;
import emu.grasscutter.database.DatabaseHelper; import java.util.concurrent.ConcurrentHashMap;
import emu.grasscutter.game.player.Player; import lombok.Getter;
import emu.grasscutter.scripts.data.SceneGadget; import lombok.Setter;
import emu.grasscutter.scripts.data.SceneGroup; import org.bson.types.ObjectId;
import lombok.Getter;
import lombok.Setter; @Entity(value = "group_instances", useDiscriminator = false)
public final class SceneGroupInstance {
@Entity(value = "group_instances", useDiscriminator = false) @Id private ObjectId id;
public final class SceneGroupInstance {
@Id private ObjectId id; @Indexed private int ownerUid; // This group is owned by the host player
@Getter private int groupId;
@Indexed private int ownerUid; //This group is owned by the host player
@Getter private int groupId; @Getter private transient SceneGroup luaGroup;
@Getter @Setter private int targetSuiteId;
@Getter private transient SceneGroup luaGroup; @Getter @Setter private int activeSuiteId;
@Getter @Setter private int targetSuiteId; @Getter private Set<Integer> deadEntities; // Config_ids
@Getter @Setter private int activeSuiteId; private boolean isCached;
@Getter private Set<Integer> deadEntities; //Config_ids
private boolean isCached; @Getter private Map<Integer, Integer> cachedGadgetStates;
@Getter private Map<String, Integer> cachedVariables;
@Getter private Map<Integer, Integer> cachedGadgetStates;
@Getter private Map<String, Integer> cachedVariables; @Getter @Setter private int lastTimeRefreshed;
@Getter @Setter private int lastTimeRefreshed; public SceneGroupInstance(SceneGroup group, Player owner) {
this.luaGroup = group;
public SceneGroupInstance(SceneGroup group, Player owner) { this.groupId = group.id;
this.luaGroup = group; this.targetSuiteId = 0;
this.groupId = group.id; this.activeSuiteId = 0;
this.targetSuiteId = 0; this.lastTimeRefreshed = 0;
this.activeSuiteId = 0; this.ownerUid = owner.getUid();
this.lastTimeRefreshed = 0; this.deadEntities = new HashSet<>();
this.ownerUid = owner.getUid(); this.cachedGadgetStates = new ConcurrentHashMap<>();
this.deadEntities = new HashSet<>(); this.cachedVariables = new ConcurrentHashMap<>();
this.cachedGadgetStates = new ConcurrentHashMap<>();
this.cachedVariables = new ConcurrentHashMap<>(); this.isCached =
false; // This is true when the group is not loaded on scene but caches suite data
this.isCached = false; //This is true when the group is not loaded on scene but caches suite data }
}
@Deprecated // Morphia only!
@Deprecated // Morphia only! SceneGroupInstance() {
SceneGroupInstance(){ this.cachedVariables = new ConcurrentHashMap<>();
this.cachedVariables = new ConcurrentHashMap<>(); this.deadEntities = new HashSet<>();
this.deadEntities = new HashSet<>(); this.cachedGadgetStates = new ConcurrentHashMap<>();
this.cachedGadgetStates = new ConcurrentHashMap<>(); }
}
public void setLuaGroup(SceneGroup group) {
public void setLuaGroup(SceneGroup group) { this.luaGroup = group;
this.luaGroup = group; this.groupId = group.id;
this.groupId = group.id; }
}
public boolean isCached() {
public boolean isCached() { return this.isCached;
return this.isCached; }
}
public void setCached(boolean value) {
public void setCached(boolean value) { this.isCached = value;
this.isCached = value; save(); // Save each time a group is registered or unregistered
save(); //Save each time a group is registered or unregistered }
}
public void cacheGadgetState(SceneGadget g, int state) {
public void cacheGadgetState(SceneGadget g, int state) { if (g.persistent) // Only cache when is persistent
if(g.persistent) //Only cache when is persistent cachedGadgetStates.put(g.config_id, state);
cachedGadgetStates.put(g.config_id, state); }
}
public int getCachedGadgetState(SceneGadget g) {
public int getCachedGadgetState(SceneGadget g) { Integer state = cachedGadgetStates.getOrDefault(g.config_id, null);
Integer state = cachedGadgetStates.getOrDefault(g.config_id, null); return (state == null) ? g.state : state;
return (state == null) ? g.state : state; }
}
public void save() {
public void save() { DatabaseHelper.saveGroupInstance(this);
DatabaseHelper.saveGroupInstance(this); }
} }
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,16 +1,16 @@
package emu.grasscutter.scripts.data; package emu.grasscutter.scripts.data;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import lombok.Setter; import lombok.Setter;
import lombok.ToString; import lombok.ToString;
@ToString @ToString
@Setter @Setter
public class SceneConfig { public class SceneConfig {
public Position vision_anchor; public Position vision_anchor;
public Position born_pos; public Position born_pos;
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; public float die_y;
} }

View File

@ -1,183 +1,206 @@
package emu.grasscutter.scripts.data; 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 lombok.Getter; import java.util.ArrayList;
import lombok.Setter; import java.util.List;
import lombok.ToString; import java.util.Map;
import org.luaj.vm2.LuaValue; import java.util.Optional;
import java.util.Random;
import javax.script.Bindings; import java.util.stream.Collectors;
import javax.script.CompiledScript; import javax.script.Bindings;
import javax.script.ScriptException; import javax.script.CompiledScript;
import javax.script.ScriptException;
import java.util.ArrayList; import lombok.Getter;
import java.util.List; import lombok.Setter;
import java.util.Map; import lombok.ToString;
import java.util.Optional; import org.luaj.vm2.LuaValue;
import java.util.Random;
import java.util.stream.Collectors; @ToString
@Setter
@ToString public final class SceneGroup {
@Setter public transient int
public final class SceneGroup { block_id; // Not an actual variable in the scripts but we will keep it here for reference
public transient int 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, 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; public List<SceneSuite> suites;
public List<SceneSuite> suites; public List<SceneVar> variables;
public List<SceneVar> variables;
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;
@Getter public boolean dynamic_load = false;
public SceneReplaceable is_replaceable;
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;
return group; return group;
} }
public boolean isLoaded() { public boolean isLoaded() {
return this.loaded; return this.loaded;
} }
public void setLoaded(boolean loaded) { public void setLoaded(boolean loaded) {
this.loaded = loaded; this.loaded = loaded;
} }
public int getBusinessType() { public int getBusinessType() {
return this.business == null ? 0 : this.business.type; return this.business == null ? 0 : this.business.type;
} }
public List<SceneGadget> getGarbageGadgets() { public List<SceneGadget> getGarbageGadgets() {
return this.garbages == null ? null : this.garbages.gadgets; return this.garbages == null ? null : this.garbages.gadgets;
} }
public CompiledScript getScript() { public CompiledScript getScript() {
return this.script; return this.script;
} }
public SceneSuite getSuiteByIndex(int index) { public SceneSuite getSuiteByIndex(int index) {
if(index < 1 || index > suites.size()) { if (index < 1 || index > suites.size()) {
return null; return null;
} }
return this.suites.get(index - 1); return this.suites.get(index - 1);
} }
public Bindings getBindings() { public Bindings getBindings() {
return this.bindings; return this.bindings;
} }
public synchronized SceneGroup load(int sceneId) { public synchronized SceneGroup load(int sceneId) {
if (this.loaded) { if (this.loaded) {
return this; return this;
} }
// Set flag here so if there is no script, we don't call this function over and over again. // Set flag here so if there is no script, we don't call this function over and over again.
this.setLoaded(true); this.setLoaded(true);
this.bindings = ScriptLoader.getEngine().createBindings(); this.bindings = ScriptLoader.getEngine().createBindings();
CompiledScript cs = ScriptLoader.getScript("Scene/" + sceneId + "/scene" + sceneId + "_group" + this.id + ".lua"); CompiledScript cs =
ScriptLoader.getScript(
if (cs == null) { "Scene/" + sceneId + "/scene" + sceneId + "_group" + this.id + ".lua");
return this;
} if (cs == null) {
return this;
this.script = cs; }
// Eval script this.script = cs;
try {
cs.eval(this.bindings); // Eval script
try {
// Set cs.eval(this.bindings);
this.monsters = ScriptLoader.getSerializer().toList(SceneMonster.class, this.bindings.get("monsters")).stream()
.collect(Collectors.toMap(x -> x.config_id, y -> y, (a, b) -> a)); // Set
this.monsters.values().forEach(m -> m.group = this); this.monsters =
ScriptLoader.getSerializer()
this.npcs = ScriptLoader.getSerializer().toList(SceneNPC.class, this.bindings.get("npcs")).stream() .toList(SceneMonster.class, this.bindings.get("monsters"))
.collect(Collectors.toMap(x -> x.config_id, y -> y, (a, b) -> a)); .stream()
this.npcs.values().forEach(m -> m.group = this); .collect(Collectors.toMap(x -> x.config_id, y -> y, (a, b) -> a));
this.monsters.values().forEach(m -> m.group = this);
this.gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, this.bindings.get("gadgets")).stream()
.collect(Collectors.toMap(x -> x.config_id, y -> y, (a, b) -> a)); this.npcs =
this.gadgets.values().forEach(m -> m.group = this); ScriptLoader.getSerializer().toList(SceneNPC.class, this.bindings.get("npcs")).stream()
.collect(Collectors.toMap(x -> x.config_id, y -> y, (a, b) -> a));
this.triggers = ScriptLoader.getSerializer().toList(SceneTrigger.class, this.bindings.get("triggers")).stream() this.npcs.values().forEach(m -> m.group = this);
.collect(Collectors.toMap(SceneTrigger::getName, y -> y, (a, b) -> a));
this.triggers.values().forEach(t -> t.currentGroup = this); this.gadgets =
ScriptLoader.getSerializer()
this.suites = ScriptLoader.getSerializer().toList(SceneSuite.class, this.bindings.get("suites")); .toList(SceneGadget.class, this.bindings.get("gadgets"))
this.regions = ScriptLoader.getSerializer().toList(SceneRegion.class, this.bindings.get("regions")).stream() .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.gadgets.values().forEach(m -> m.group = this);
this.init_config = ScriptLoader.getSerializer().toObject(SceneInitConfig.class, this.bindings.get("init_config")); this.triggers =
ScriptLoader.getSerializer()
// Garbages // TODO: fix properly later .toList(SceneTrigger.class, this.bindings.get("triggers"))
Object garbagesValue = this.bindings.get("garbages"); .stream()
if (garbagesValue instanceof LuaValue garbagesTable) { .collect(Collectors.toMap(SceneTrigger::getName, y -> y, (a, b) -> a));
this.garbages = new SceneGarbage(); this.triggers.values().forEach(t -> t.currentGroup = this);
if (garbagesTable.checktable().get("gadgets") != LuaValue.NIL) {
this.garbages.gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, garbagesTable.checktable().get("gadgets").checktable()); this.suites =
this.garbages.gadgets.forEach(m -> m.group = this); ScriptLoader.getSerializer().toList(SceneSuite.class, this.bindings.get("suites"));
} this.regions =
} ScriptLoader.getSerializer()
.toList(SceneRegion.class, this.bindings.get("regions"))
// Add variables to suite .stream()
this.variables = ScriptLoader.getSerializer().toList(SceneVar.class, this.bindings.get("variables")); .collect(Collectors.toMap(x -> x.config_id, y -> y, (a, b) -> a));
this.regions.values().forEach(m -> m.group = this);
// Add monsters and gadgets to suite
this.suites.forEach(i -> i.init(this)); this.init_config =
ScriptLoader.getSerializer()
} catch (ScriptException e) { .toObject(SceneInitConfig.class, this.bindings.get("init_config"));
Grasscutter.getLogger().error("An error occurred while loading group " + this.id + " in scene " + sceneId + ".", e);
} // Garbages // TODO: fix properly later
Object garbagesValue = this.bindings.get("garbages");
Grasscutter.getLogger().debug("Successfully loaded group {} in scene {}.", this.id, sceneId); if (garbagesValue instanceof LuaValue garbagesTable) {
return this; this.garbages = new SceneGarbage();
} if (garbagesTable.checktable().get("gadgets") != LuaValue.NIL) {
this.garbages.gadgets =
public int findInitSuiteIndex(int exclude_index) { //TODO: Investigate end index ScriptLoader.getSerializer()
if (init_config == null) return 1; .toList(
if (init_config.io_type == 1) return init_config.suite; //IO TYPE FLOW SceneGadget.class, garbagesTable.checktable().get("gadgets").checktable());
if (init_config.rand_suite) { this.garbages.gadgets.forEach(m -> m.group = this);
if (suites.size() == 1) { }
return init_config.suite; }
} else {
List<Integer> randSuiteList = new ArrayList<>(); // Add variables to suite
for (int i = 0; i < suites.size(); i++) { this.variables =
if (i == exclude_index) continue; ScriptLoader.getSerializer().toList(SceneVar.class, this.bindings.get("variables"));
var suite = suites.get(i); // Add monsters and gadgets to suite
for(int j = 0; j < suite.rand_weight; j++) randSuiteList.add(i); this.suites.forEach(i -> i.init(this));
}
} catch (ScriptException e) {
return randSuiteList.get(new Random().nextInt(randSuiteList.size())); Grasscutter.getLogger()
} .error(
} "An error occurred while loading group " + this.id + " in scene " + sceneId + ".", e);
return init_config.suite; }
}
Grasscutter.getLogger().debug("Successfully loaded group {} in scene {}.", this.id, sceneId);
public Optional<SceneBossChest> searchBossChestInGroup() { return this;
return this.gadgets.values().stream() }
.filter(g -> g.boss_chest != null && g.boss_chest.monster_config_id > 0)
.map(g -> g.boss_chest) public int findInitSuiteIndex(int exclude_index) { // TODO: Investigate end index
.findFirst(); 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() {
return this.gadgets.values().stream()
.filter(g -> g.boss_chest != null && g.boss_chest.monster_config_id > 0)
.map(g -> g.boss_chest)
.findFirst();
}
}

View File

@ -1,13 +1,13 @@
package emu.grasscutter.scripts.data; package emu.grasscutter.scripts.data;
import lombok.Setter; import lombok.Setter;
import lombok.ToString; import lombok.ToString;
@ToString @ToString
@Setter @Setter
public final 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 int io_type;
public boolean rand_suite; public boolean rand_suite;
} }

View File

@ -1,15 +1,15 @@
package emu.grasscutter.scripts.data; package emu.grasscutter.scripts.data;
import lombok.Setter; import lombok.Setter;
import lombok.ToString; import lombok.ToString;
@ToString @ToString
@Setter @Setter
public class SceneMonster extends SceneObject{ 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 boolean disableWander; public boolean disableWander;
public int title_id; public int title_id;
public int special_name_id; public int special_name_id;
} }

View File

@ -1,19 +1,19 @@
package emu.grasscutter.scripts.data; package emu.grasscutter.scripts.data;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import lombok.Setter; import lombok.Setter;
import lombok.ToString; import lombok.ToString;
@ToString @ToString
@Setter @Setter
public abstract 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 int vision_level = 0;
public Position pos; public Position pos;
public Position rot; public Position rot;
/** not set by lua */ /** not set by lua */
public transient SceneGroup group; public transient SceneGroup group;
} }

View File

@ -1,63 +1,61 @@
package emu.grasscutter.scripts.data; 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;
@ToString
@ToString @Setter
@Setter public class SceneSuite {
public class SceneSuite { // make it refer the default empty list to avoid NPE caused by some group
// make it refer the default empty list to avoid NPE caused by some group public List<Integer> monsters = List.of();
public List<Integer> monsters = List.of(); public List<Integer> gadgets = List.of();
public List<Integer> gadgets = List.of(); 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 boolean ban_refresh = false;
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(); public transient List<SceneTrigger> sceneTriggers = List.of();
public transient List<SceneTrigger> sceneTriggers = List.of(); 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) {
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) {
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) {
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) {
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,45 +1,57 @@
package emu.grasscutter.scripts.data; package emu.grasscutter.scripts.data;
import lombok.*; import lombok.*;
@Setter @Setter
@Getter @Getter
@NoArgsConstructor @NoArgsConstructor
// todo find way to deserialize from lua with final fields, maybe with the help of Builder? // todo find way to deserialize from lua with final fields, maybe with the help of Builder?
public final class SceneTrigger { public final class SceneTrigger {
private String name; private String name;
private int config_id; private int config_id;
private int event; private int event;
private int trigger_count = 1; private int trigger_count = 1;
private String source; private String source;
private String condition; private String condition;
private String action; private String action;
private String tag; private String tag;
public transient SceneGroup currentGroup; public transient SceneGroup currentGroup;
@Override @Override
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); } else return super.equals(obj);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return name.hashCode(); return name.hashCode();
} }
@Override @Override
public String toString() { public String toString() {
return "SceneTrigger{" + return "SceneTrigger{"
"name='" + name + '\'' + + "name='"
", config_id=" + config_id + + name
", event=" + event + + '\''
", source='" + source + '\'' + + ", config_id="
", condition='" + condition + '\'' + + config_id
", action='" + action + '\'' + + ", event="
", trigger_count='" + trigger_count + '\'' + + event
'}'; + ", source='"
} + source
} + '\''
+ ", condition='"
+ condition
+ '\''
+ ", action='"
+ action
+ '\''
+ ", trigger_count='"
+ trigger_count
+ '\''
+ '}';
}
}

View File

@ -1,90 +1,90 @@
package emu.grasscutter.scripts.data; package emu.grasscutter.scripts.data;
public class ScriptArgs { public class ScriptArgs {
public int param1; public int param1;
public int param2; public int param2;
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 int group_id;
public String source; // source string, used for timers public String source; // source string, used for timers
public int type; // lua event type, used by scripts and the ScriptManager public int type; // lua event type, used by scripts and the ScriptManager
public ScriptArgs(int groupId, int eventType) { public ScriptArgs(int groupId, int eventType) {
this(groupId, eventType, 0,0); this(groupId, eventType, 0, 0);
} }
public ScriptArgs(int groupId, int eventType, int param1) { public ScriptArgs(int groupId, int eventType, int param1) {
this(groupId, eventType, param1,0); this(groupId, eventType, param1, 0);
} }
public ScriptArgs(int groupId, int eventType, int param1, int param2) { public ScriptArgs(int groupId, int eventType, int param1, int param2) {
this.type = eventType; this.type = eventType;
this.param1 = param1; this.param1 = param1;
this.param2 = param2; this.param2 = param2;
this.group_id = groupId; this.group_id = groupId;
} }
public int getParam1() { public int getParam1() {
return param1; return param1;
} }
public ScriptArgs setParam1(int param1) { public ScriptArgs setParam1(int param1) {
this.param1 = param1; this.param1 = param1;
return this; return this;
} }
public int getParam2() { public int getParam2() {
return param2; return param2;
} }
public ScriptArgs setParam2(int param2) { public ScriptArgs setParam2(int param2) {
this.param2 = param2; this.param2 = param2;
return this; return this;
} }
public int getParam3() { public int getParam3() {
return param3; return param3;
} }
public ScriptArgs setParam3(int param3) { public ScriptArgs setParam3(int param3) {
this.param3 = param3; this.param3 = param3;
return this; return this;
} }
public int getSourceEntityId() { public int getSourceEntityId() {
return source_eid; return source_eid;
} }
public ScriptArgs setSourceEntityId(int source_eid) { public ScriptArgs setSourceEntityId(int source_eid) {
this.source_eid = source_eid; this.source_eid = source_eid;
return this; return this;
} }
public int getTargetEntityId() { public int getTargetEntityId() {
return target_eid; return target_eid;
} }
public ScriptArgs setTargetEntityId(int target_eid) { public ScriptArgs setTargetEntityId(int target_eid) {
this.target_eid = target_eid; this.target_eid = target_eid;
return this; return this;
} }
public String getEventSource() { public String getEventSource() {
return source; return source;
} }
public ScriptArgs setEventSource(String source) { public ScriptArgs setEventSource(String source) {
this.source = source; this.source = source;
return this; return this;
} }
public int getGroupId() { public int getGroupId() {
return group_id; return group_id;
} }
public ScriptArgs setGroupId(int group_id) { public ScriptArgs setGroupId(int group_id) {
this.group_id = group_id; this.group_id = group_id;
return this; return this;
} }
} }

View File

@ -1,92 +1,102 @@
package emu.grasscutter.scripts.service; package emu.grasscutter.scripts.service;
import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.scripts.SceneScriptManager; import emu.grasscutter.scripts.SceneScriptManager;
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.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.List;
import java.util.ArrayList; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.List; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger; public final class ScriptMonsterTideService {
private final SceneScriptManager sceneScriptManager;
public final class ScriptMonsterTideService { private final SceneGroup currentGroup;
private final SceneScriptManager sceneScriptManager; private final AtomicInteger monsterAlive;
private final SceneGroup currentGroup; private final AtomicInteger monsterTideCount;
private final AtomicInteger monsterAlive; private final AtomicInteger monsterKillCount;
private final AtomicInteger monsterTideCount; private final int monsterSceneLimit;
private final AtomicInteger monsterKillCount; private final ConcurrentLinkedQueue<Integer> monsterConfigOrders;
private final int monsterSceneLimit; private final List<Integer> monsterConfigIds;
private final ConcurrentLinkedQueue<Integer> monsterConfigOrders; private final OnMonsterCreated onMonsterCreated = new OnMonsterCreated();
private final List<Integer> monsterConfigIds; private final OnMonsterDead onMonsterDead = new OnMonsterDead();
private final OnMonsterCreated onMonsterCreated= new OnMonsterCreated();
private final OnMonsterDead onMonsterDead= new OnMonsterDead(); public ScriptMonsterTideService(
SceneScriptManager sceneScriptManager,
public ScriptMonsterTideService(SceneScriptManager sceneScriptManager, SceneGroup group,
SceneGroup group, int tideCount, int monsterSceneLimit, Integer[] ordersConfigId){ int tideCount,
this.sceneScriptManager = sceneScriptManager; int monsterSceneLimit,
this.currentGroup = group; Integer[] ordersConfigId) {
this.monsterSceneLimit = monsterSceneLimit; this.sceneScriptManager = sceneScriptManager;
this.monsterTideCount = new AtomicInteger(tideCount); this.currentGroup = group;
this.monsterKillCount = new AtomicInteger(0); this.monsterSceneLimit = monsterSceneLimit;
this.monsterAlive = new AtomicInteger(0); this.monsterTideCount = new AtomicInteger(tideCount);
this.monsterConfigOrders = new ConcurrentLinkedQueue<>(List.of(ordersConfigId)); this.monsterKillCount = new AtomicInteger(0);
this.monsterConfigIds = List.of(ordersConfigId); this.monsterAlive = new AtomicInteger(0);
this.monsterConfigOrders = new ConcurrentLinkedQueue<>(List.of(ordersConfigId));
this.sceneScriptManager.getScriptMonsterSpawnService().addMonsterCreatedListener(onMonsterCreated); this.monsterConfigIds = List.of(ordersConfigId);
this.sceneScriptManager.getScriptMonsterSpawnService().addMonsterDeadListener(onMonsterDead);
// spawn the first turn this.sceneScriptManager
for (int i = 0; i < this.monsterSceneLimit; i++) { .getScriptMonsterSpawnService()
sceneScriptManager.addEntity(this.sceneScriptManager.createMonster(group.id, group.block_id, getNextMonster())); .addMonsterCreatedListener(onMonsterCreated);
} this.sceneScriptManager.getScriptMonsterSpawnService().addMonsterDeadListener(onMonsterDead);
} // spawn the first turn
for (int i = 0; i < this.monsterSceneLimit; i++) {
public class OnMonsterCreated implements ScriptMonsterListener{ sceneScriptManager.addEntity(
@Override this.sceneScriptManager.createMonster(group.id, group.block_id, getNextMonster()));
public void onNotify(EntityMonster sceneMonster) { }
if(monsterConfigIds.contains(sceneMonster.getConfigId()) && monsterSceneLimit > 0){ }
monsterAlive.incrementAndGet();
monsterTideCount.decrementAndGet(); public class OnMonsterCreated implements ScriptMonsterListener {
} @Override
} public void onNotify(EntityMonster sceneMonster) {
} if (monsterConfigIds.contains(sceneMonster.getConfigId()) && monsterSceneLimit > 0) {
monsterAlive.incrementAndGet();
public SceneMonster getNextMonster(){ monsterTideCount.decrementAndGet();
var nextId = this.monsterConfigOrders.poll(); }
if(currentGroup.monsters.containsKey(nextId)){ }
return currentGroup.monsters.get(nextId); }
}
// TODO some monster config_id do not exist in groups, so temporarily set it to the first public SceneMonster getNextMonster() {
return currentGroup.monsters.values().stream().findFirst().orElse(null); var nextId = this.monsterConfigOrders.poll();
} if (currentGroup.monsters.containsKey(nextId)) {
return currentGroup.monsters.get(nextId);
public class OnMonsterDead implements ScriptMonsterListener { }
@Override // TODO some monster config_id do not exist in groups, so temporarily set it to the first
public void onNotify(EntityMonster sceneMonster) { return currentGroup.monsters.values().stream().findFirst().orElse(null);
if (monsterSceneLimit <= 0) { }
return;
} public class OnMonsterDead implements ScriptMonsterListener {
if (monsterAlive.decrementAndGet() >= monsterSceneLimit) { @Override
// maybe not happen public void onNotify(EntityMonster sceneMonster) {
return; if (monsterSceneLimit <= 0) {
} return;
monsterKillCount.incrementAndGet(); }
if (monsterTideCount.get() > 0) { if (monsterAlive.decrementAndGet() >= monsterSceneLimit) {
// add more // maybe not happen
sceneScriptManager.addEntity(sceneScriptManager.createMonster(currentGroup.id, currentGroup.block_id, getNextMonster())); return;
} }
// spawn the last turn of monsters monsterKillCount.incrementAndGet();
// fix the 5-2 if (monsterTideCount.get() > 0) {
sceneScriptManager.callEvent(new ScriptArgs(currentGroup.id, EventType.EVENT_MONSTER_TIDE_DIE, monsterKillCount.get())); // add more
} sceneScriptManager.addEntity(
sceneScriptManager.createMonster(
} currentGroup.id, currentGroup.block_id, getNextMonster()));
}
public void unload(){ // spawn the last turn of monsters
this.sceneScriptManager.getScriptMonsterSpawnService().removeMonsterCreatedListener(onMonsterCreated); // fix the 5-2
this.sceneScriptManager.getScriptMonsterSpawnService().removeMonsterDeadListener(onMonsterDead); sceneScriptManager.callEvent(
} new ScriptArgs(
} currentGroup.id, EventType.EVENT_MONSTER_TIDE_DIE, monsterKillCount.get()));
}
}
public void unload() {
this.sceneScriptManager
.getScriptMonsterSpawnService()
.removeMonsterCreatedListener(onMonsterCreated);
this.sceneScriptManager.getScriptMonsterSpawnService().removeMonsterDeadListener(onMonsterDead);
}
}

View File

@ -1,24 +1,27 @@
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.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 javax.annotation.Nullable;
import lombok.Setter; import lombok.Getter;
import lombok.Setter;
import javax.annotation.Nullable;
public final class EntityDamageEvent extends EntityEvent implements Cancellable {
public final class EntityDamageEvent extends EntityEvent implements Cancellable { @Getter @Setter private float damage;
@Getter @Setter private float damage; @Getter @Setter private ElementType attackElementType;
@Getter @Setter private ElementType attackElementType; @Getter @Nullable private final GameEntity damager;
@Getter @Nullable private final GameEntity damager;
public EntityDamageEvent(
public EntityDamageEvent(GameEntity entity, float damage, ElementType attackElementType, @Nullable GameEntity damager) { GameEntity entity,
super(entity); float damage,
ElementType attackElementType,
this.damage = damage; @Nullable GameEntity damager) {
this.attackElementType = attackElementType; super(entity);
this.damager = damager;
} this.damage = damage;
} this.attackElementType = attackElementType;
this.damager = damager;
}
}

View File

@ -1,279 +1,279 @@
package emu.grasscutter.server.game; package emu.grasscutter.server.game;
import static emu.grasscutter.config.Configuration.GAME_INFO; import static emu.grasscutter.config.Configuration.GAME_INFO;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.GameConstants; import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.Account; import emu.grasscutter.game.Account;
import emu.grasscutter.game.battlepass.BattlePassSystem; import emu.grasscutter.game.battlepass.BattlePassSystem;
import emu.grasscutter.game.chat.ChatSystem; import emu.grasscutter.game.chat.ChatSystem;
import emu.grasscutter.game.chat.ChatSystemHandler; 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.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;
import emu.grasscutter.game.managers.cooking.CookingManager; import emu.grasscutter.game.managers.cooking.CookingManager;
import emu.grasscutter.game.managers.energy.EnergyManager; import emu.grasscutter.game.managers.energy.EnergyManager;
import emu.grasscutter.game.managers.stamina.StaminaManager; import emu.grasscutter.game.managers.stamina.StaminaManager;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.QuestSystem; import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.shop.ShopSystem; import emu.grasscutter.game.shop.ShopSystem;
import emu.grasscutter.game.systems.AnnouncementSystem; import emu.grasscutter.game.systems.AnnouncementSystem;
import emu.grasscutter.game.systems.InventorySystem; import emu.grasscutter.game.systems.InventorySystem;
import emu.grasscutter.game.systems.MultiplayerSystem; import emu.grasscutter.game.systems.MultiplayerSystem;
import emu.grasscutter.game.tower.TowerSystem; import emu.grasscutter.game.tower.TowerSystem;
import emu.grasscutter.game.world.World; import emu.grasscutter.game.world.World;
import emu.grasscutter.game.world.WorldDataSystem; import emu.grasscutter.game.world.WorldDataSystem;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
import emu.grasscutter.server.event.game.ServerTickEvent; import emu.grasscutter.server.event.game.ServerTickEvent;
import emu.grasscutter.server.event.internal.ServerStartEvent; import emu.grasscutter.server.event.internal.ServerStartEvent;
import emu.grasscutter.server.event.internal.ServerStopEvent; import emu.grasscutter.server.event.internal.ServerStopEvent;
import emu.grasscutter.server.event.types.ServerEvent; import emu.grasscutter.server.event.types.ServerEvent;
import emu.grasscutter.server.scheduler.ServerTaskScheduler; import emu.grasscutter.server.scheduler.ServerTaskScheduler;
import emu.grasscutter.task.TaskMap; import emu.grasscutter.task.TaskMap;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.time.Instant; import java.time.Instant;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import kcp.highway.ChannelConfig; import kcp.highway.ChannelConfig;
import kcp.highway.KcpServer; import kcp.highway.KcpServer;
import lombok.Getter; import lombok.Getter;
@Getter @Getter
public final class GameServer extends KcpServer { public final class GameServer extends KcpServer {
// Game server base // Game server base
private final InetSocketAddress address; private final InetSocketAddress address;
private final GameServerPacketHandler packetHandler; private final GameServerPacketHandler packetHandler;
private final Map<Integer, Player> players; private final Map<Integer, Player> players;
private final Set<World> worlds; private final Set<World> worlds;
// Server systems // Server systems
private final InventorySystem inventorySystem; private final InventorySystem inventorySystem;
private final GachaSystem gachaSystem; private final GachaSystem gachaSystem;
private final ShopSystem shopSystem; private final ShopSystem shopSystem;
private final MultiplayerSystem multiplayerSystem; private final MultiplayerSystem multiplayerSystem;
private final DungeonSystem dungeonSystem; private final DungeonSystem dungeonSystem;
private final ExpeditionSystem expeditionSystem; private final ExpeditionSystem expeditionSystem;
private final DropSystem dropSystem; private final DropSystem dropSystem;
private final WorldDataSystem worldDataSystem; private final WorldDataSystem worldDataSystem;
private final BattlePassSystem battlePassSystem; private final BattlePassSystem battlePassSystem;
private final CombineManger combineSystem; private final CombineManger combineSystem;
private final TowerSystem towerSystem; private final TowerSystem towerSystem;
private final AnnouncementSystem announcementSystem; private final AnnouncementSystem announcementSystem;
private final QuestSystem questSystem; private final QuestSystem questSystem;
// Extra // Extra
private final ServerTaskScheduler scheduler; private final ServerTaskScheduler scheduler;
private final TaskMap taskMap; private final TaskMap taskMap;
private ChatSystemHandler chatManager; private ChatSystemHandler chatManager;
public GameServer() { public GameServer() {
this(getAdapterInetSocketAddress()); this(getAdapterInetSocketAddress());
} }
public GameServer(InetSocketAddress address) { public GameServer(InetSocketAddress address) {
ChannelConfig channelConfig = new ChannelConfig(); ChannelConfig channelConfig = new ChannelConfig();
channelConfig.nodelay(true, GAME_INFO.kcpInterval, 2, true); channelConfig.nodelay(true, GAME_INFO.kcpInterval, 2, true);
channelConfig.setMtu(1400); channelConfig.setMtu(1400);
channelConfig.setSndwnd(256); channelConfig.setSndwnd(256);
channelConfig.setRcvwnd(256); channelConfig.setRcvwnd(256);
channelConfig.setTimeoutMillis(30 * 1000); // 30s channelConfig.setTimeoutMillis(30 * 1000); // 30s
channelConfig.setUseConvChannel(true); channelConfig.setUseConvChannel(true);
channelConfig.setAckNoDelay(false); channelConfig.setAckNoDelay(false);
this.init(GameSessionManager.getListener(), channelConfig, address); this.init(GameSessionManager.getListener(), channelConfig, address);
EnergyManager.initialize(); EnergyManager.initialize();
StaminaManager.initialize(); StaminaManager.initialize();
CookingManager.initialize(); CookingManager.initialize();
CookingCompoundManager.initialize(); CookingCompoundManager.initialize();
CombineManger.initialize(); CombineManger.initialize();
// Game Server base // Game Server base
this.address = address; this.address = address;
this.packetHandler = new GameServerPacketHandler(PacketHandler.class); this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
this.players = new ConcurrentHashMap<>(); this.players = new ConcurrentHashMap<>();
this.worlds = Collections.synchronizedSet(new HashSet<>()); this.worlds = Collections.synchronizedSet(new HashSet<>());
// Extra // Extra
this.scheduler = new ServerTaskScheduler(); this.scheduler = new ServerTaskScheduler();
this.taskMap = new TaskMap(true); this.taskMap = new TaskMap(true);
// Create game systems // Create game systems
this.inventorySystem = new InventorySystem(this); this.inventorySystem = new InventorySystem(this);
this.gachaSystem = new GachaSystem(this); this.gachaSystem = new GachaSystem(this);
this.shopSystem = new ShopSystem(this); this.shopSystem = new ShopSystem(this);
this.multiplayerSystem = new MultiplayerSystem(this); this.multiplayerSystem = new MultiplayerSystem(this);
this.dungeonSystem = new DungeonSystem(this); this.dungeonSystem = new DungeonSystem(this);
this.dropSystem = new DropSystem(this); this.dropSystem = new DropSystem(this);
this.expeditionSystem = new ExpeditionSystem(this); this.expeditionSystem = new ExpeditionSystem(this);
this.combineSystem = new CombineManger(this); this.combineSystem = new CombineManger(this);
this.towerSystem = new TowerSystem(this); this.towerSystem = new TowerSystem(this);
this.worldDataSystem = new WorldDataSystem(this); this.worldDataSystem = new WorldDataSystem(this);
this.battlePassSystem = new BattlePassSystem(this); this.battlePassSystem = new BattlePassSystem(this);
this.announcementSystem = new AnnouncementSystem(this); this.announcementSystem = new AnnouncementSystem(this);
this.questSystem = new QuestSystem(this); this.questSystem = new QuestSystem(this);
// Chata manager // Chata manager
this.chatManager = new ChatSystem(this); this.chatManager = new ChatSystem(this);
// Hook into shutdown event. // Hook into shutdown event.
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown)); Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
} }
private static InetSocketAddress getAdapterInetSocketAddress() { private static InetSocketAddress getAdapterInetSocketAddress() {
InetSocketAddress inetSocketAddress; InetSocketAddress inetSocketAddress;
if (GAME_INFO.bindAddress.equals("")) { if (GAME_INFO.bindAddress.equals("")) {
inetSocketAddress = new InetSocketAddress(GAME_INFO.bindPort); inetSocketAddress = new InetSocketAddress(GAME_INFO.bindPort);
} else { } else {
inetSocketAddress = new InetSocketAddress(GAME_INFO.bindAddress, GAME_INFO.bindPort); inetSocketAddress = new InetSocketAddress(GAME_INFO.bindAddress, GAME_INFO.bindPort);
} }
return inetSocketAddress; return inetSocketAddress;
} }
@Deprecated @Deprecated
public ChatSystemHandler getChatManager() { public ChatSystemHandler getChatManager() {
return chatManager; return chatManager;
} }
@Deprecated @Deprecated
public void setChatManager(ChatSystemHandler chatManager) { public void setChatManager(ChatSystemHandler chatManager) {
this.chatManager = chatManager; this.chatManager = chatManager;
} }
public ChatSystemHandler getChatSystem() { public ChatSystemHandler getChatSystem() {
return chatManager; return chatManager;
} }
public void setChatSystem(ChatSystemHandler chatManager) { public void setChatSystem(ChatSystemHandler chatManager) {
this.chatManager = chatManager; this.chatManager = chatManager;
} }
public void registerPlayer(Player player) { public void registerPlayer(Player player) {
getPlayers().put(player.getUid(), player); getPlayers().put(player.getUid(), player);
} }
public Player getPlayerByUid(int id) { public Player getPlayerByUid(int id) {
return this.getPlayerByUid(id, false); return this.getPlayerByUid(id, false);
} }
public Player getPlayerByUid(int id, boolean allowOfflinePlayers) { public Player getPlayerByUid(int id, boolean allowOfflinePlayers) {
// Console check // Console check
if (id == GameConstants.SERVER_CONSOLE_UID) { if (id == GameConstants.SERVER_CONSOLE_UID) {
return null; return null;
} }
// Get from online players // Get from online players
Player player = this.getPlayers().get(id); Player player = this.getPlayers().get(id);
if (!allowOfflinePlayers) { if (!allowOfflinePlayers) {
return player; return player;
} }
// Check database if character isnt here // Check database if character isnt here
if (player == null) { if (player == null) {
player = DatabaseHelper.getPlayerByUid(id); player = DatabaseHelper.getPlayerByUid(id);
} }
return player; return player;
} }
public Player getPlayerByAccountId(String accountId) { public Player getPlayerByAccountId(String accountId) {
Optional<Player> playerOpt = Optional<Player> playerOpt =
getPlayers().values().stream() getPlayers().values().stream()
.filter(player -> player.getAccount().getId().equals(accountId)) .filter(player -> player.getAccount().getId().equals(accountId))
.findFirst(); .findFirst();
return playerOpt.orElse(null); return playerOpt.orElse(null);
} }
public SocialDetail.Builder getSocialDetailByUid(int id) { public SocialDetail.Builder getSocialDetailByUid(int id) {
// Get from online players // Get from online players
Player player = this.getPlayerByUid(id, true); Player player = this.getPlayerByUid(id, true);
if (player == null) { if (player == null) {
return null; return null;
} }
return player.getSocialDetail(); return player.getSocialDetail();
} }
public Account getAccountByName(String username) { public Account getAccountByName(String username) {
Optional<Player> playerOpt = Optional<Player> playerOpt =
getPlayers().values().stream() getPlayers().values().stream()
.filter(player -> player.getAccount().getUsername().equals(username)) .filter(player -> player.getAccount().getUsername().equals(username))
.findFirst(); .findFirst();
if (playerOpt.isPresent()) { if (playerOpt.isPresent()) {
return playerOpt.get().getAccount(); return playerOpt.get().getAccount();
} }
return DatabaseHelper.getAccountByName(username); return DatabaseHelper.getAccountByName(username);
} }
public synchronized void onTick() { public synchronized void onTick() {
var tickStart = Instant.now(); var tickStart = Instant.now();
// Tick worlds. // Tick worlds.
this.worlds.removeIf(World::onTick); this.worlds.removeIf(World::onTick);
// Tick players. // Tick players.
this.players.values().forEach(Player::onTick); this.players.values().forEach(Player::onTick);
// Tick scheduler. // Tick scheduler.
this.getScheduler().runTasks(); this.getScheduler().runTasks();
// Call server tick event. // Call server tick event.
ServerTickEvent event = new ServerTickEvent(tickStart, Instant.now()); ServerTickEvent event = new ServerTickEvent(tickStart, Instant.now());
event.call(); event.call();
} }
public void registerWorld(World world) { public void registerWorld(World world) {
this.getWorlds().add(world); this.getWorlds().add(world);
} }
public void deregisterWorld(World world) { public void deregisterWorld(World world) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
} }
public void start() { public void start() {
// Schedule game loop. // Schedule game loop.
Timer gameLoop = new Timer(); Timer gameLoop = new Timer();
gameLoop.scheduleAtFixedRate( gameLoop.scheduleAtFixedRate(
new TimerTask() { new TimerTask() {
@Override @Override
public void run() { public void run() {
try { try {
onTick(); onTick();
} catch (Exception e) { } catch (Exception e) {
Grasscutter.getLogger().error(translate("messages.game.game_update_error"), e); Grasscutter.getLogger().error(translate("messages.game.game_update_error"), e);
} }
} }
}, },
new Date(), new Date(),
1000L); 1000L);
Grasscutter.getLogger().info(translate("messages.status.free_software")); Grasscutter.getLogger().info(translate("messages.status.free_software"));
Grasscutter.getLogger() Grasscutter.getLogger()
.info(translate("messages.game.address_bind", GAME_INFO.accessAddress, address.getPort())); .info(translate("messages.game.address_bind", GAME_INFO.accessAddress, address.getPort()));
ServerStartEvent event = new ServerStartEvent(ServerEvent.Type.GAME, OffsetDateTime.now()); ServerStartEvent event = new ServerStartEvent(ServerEvent.Type.GAME, OffsetDateTime.now());
event.call(); event.call();
} }
public void onServerShutdown() { public void onServerShutdown() {
ServerStopEvent event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now()); ServerStopEvent event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now());
event.call(); event.call();
// Kick and save all players // Kick and save all players
List<Player> list = new ArrayList<>(this.getPlayers().size()); List<Player> list = new ArrayList<>(this.getPlayers().size());
list.addAll(this.getPlayers().values()); list.addAll(this.getPlayers().values());
for (Player player : list) { for (Player player : list) {
player.getSession().close(); player.getSession().close();
} }
} }
} }

View File

@ -1,14 +1,14 @@
//package emu.grasscutter.server.packet.recv; // package emu.grasscutter.server.packet.recv;
// //
//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.server.game.GameSession; // import emu.grasscutter.server.game.GameSession;
// //
//@Opcodes(PacketOpcodes.AddCustomTeamReq) // @Opcodes(PacketOpcodes.AddCustomTeamReq)
//public class HandlerAddCustomTeamReq extends PacketHandler { // public class HandlerAddCustomTeamReq 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 {
// session.getPlayer().getTeamManager().addNewCustomTeam(); // session.getPlayer().getTeamManager().addNewCustomTeam();
// } // }
//} // }

View File

@ -1,26 +1,25 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.quest.enums.QuestContent; 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;
import emu.grasscutter.net.proto.AddQuestContentProgressReqOuterClass.AddQuestContentProgressReq; 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;
@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 = 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
var type = QuestContent.getContentTriggerByValue(req.getContentType()); var type = QuestContent.getContentTriggerByValue(req.getContentType());
if(type != null) { if (type != null) {
session.getPlayer().getQuestManager().queueEvent(type, req.getParam()); session.getPlayer().getQuestManager().queueEvent(type, req.getParam());
} }
session.send(new PacketAddQuestContentProgressRsp(req.getContentType())); session.send(new PacketAddQuestContentProgressRsp(req.getContentType()));
} }
}
}

View File

@ -1,39 +1,38 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
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.PacketAvatarChangeElementTypeRsp; import emu.grasscutter.server.packet.send.PacketAvatarChangeElementTypeRsp;
import lombok.val; import lombok.val;
/** /** Changes the currently active avatars Element if possible */
* Changes the currently active avatars Element if possible @Opcodes(PacketOpcodes.AvatarChangeElementTypeReq)
*/ public class HandlerAvatarChangeElementTypeReq extends PacketHandler {
@Opcodes(PacketOpcodes.AvatarChangeElementTypeReq)
public class HandlerAvatarChangeElementTypeReq extends PacketHandler { @Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
@Override var req = AvatarChangeElementTypeReq.parseFrom(payload);
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { var area = GameData.getWorldAreaDataMap().get(req.getAreaId());
var req = AvatarChangeElementTypeReq.parseFrom(payload);
var area = GameData.getWorldAreaDataMap().get(req.getAreaId()); if (area == null
|| area.getElementType() == null
if (area == null || area.getElementType() == null || area.getElementType().getDepotIndex() <= 0) { || area.getElementType().getDepotIndex() <= 0) {
session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE)); session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE));
return; return;
} }
val avatar = session.getPlayer().getTeamManager().getCurrentAvatarEntity().getAvatar(); val avatar = session.getPlayer().getTeamManager().getCurrentAvatarEntity().getAvatar();
if (!avatar.changeElement(area.getElementType())) { if (!avatar.changeElement(area.getElementType())) {
session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE)); session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE));
return; return;
} }
// Success // Success
session.send(new PacketAvatarChangeElementTypeRsp()); session.send(new PacketAvatarChangeElementTypeRsp());
} }
}
}

View File

@ -1,26 +1,26 @@
//package emu.grasscutter.server.packet.recv; // package emu.grasscutter.server.packet.recv;
// //
//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.Unk2700BEDLIGJANCJClientReq; // import emu.grasscutter.net.proto.Unk2700BEDLIGJANCJClientReq;
//import emu.grasscutter.server.game.GameSession; // import emu.grasscutter.server.game.GameSession;
//import emu.grasscutter.server.packet.send.PacketChangeHomeBgmNotify; // import emu.grasscutter.server.packet.send.PacketChangeHomeBgmNotify;
//import emu.grasscutter.server.packet.send.PacketChangeHomeBgmRsp; // import emu.grasscutter.server.packet.send.PacketChangeHomeBgmRsp;
// //
//@Opcodes(PacketOpcodes.Unk2700_BEDLIGJANCJ_ClientReq) // @Opcodes(PacketOpcodes.Unk2700_BEDLIGJANCJ_ClientReq)
//public class HandlerChangeHomeBgmReq extends PacketHandler { // public class HandlerChangeHomeBgmReq 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 = Unk2700BEDLIGJANCJClientReq.Unk2700_BEDLIGJANCJ_ClientReq.parseFrom(payload); // var req = Unk2700BEDLIGJANCJClientReq.Unk2700_BEDLIGJANCJ_ClientReq.parseFrom(payload);
// //
// int homeBgmId = req.getUnk2700BJHAMKKECEI(); // int homeBgmId = req.getUnk2700BJHAMKKECEI();
// var home = session.getPlayer().getHome(); // var home = session.getPlayer().getHome();
// //
// home.getHomeSceneItem(session.getPlayer().getSceneId()).setHomeBgmId(homeBgmId); // home.getHomeSceneItem(session.getPlayer().getSceneId()).setHomeBgmId(homeBgmId);
// home.save(); // home.save();
// //
// session.send(new PacketChangeHomeBgmNotify(homeBgmId)); // session.send(new PacketChangeHomeBgmNotify(homeBgmId));
// session.send(new PacketChangeHomeBgmRsp()); // session.send(new PacketChangeHomeBgmRsp());
// } // }
//} // }

View File

@ -1,191 +1,191 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
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.AttackResultOuterClass.AttackResult; import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
import emu.grasscutter.net.proto.CombatInvocationsNotifyOuterClass.CombatInvocationsNotify; import emu.grasscutter.net.proto.CombatInvocationsNotifyOuterClass.CombatInvocationsNotify;
import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry; import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
import emu.grasscutter.net.proto.EntityMoveInfoOuterClass.EntityMoveInfo; import emu.grasscutter.net.proto.EntityMoveInfoOuterClass.EntityMoveInfo;
import emu.grasscutter.net.proto.EvtAnimatorParameterInfoOuterClass.EvtAnimatorParameterInfo; import emu.grasscutter.net.proto.EvtAnimatorParameterInfoOuterClass.EvtAnimatorParameterInfo;
import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo; import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo;
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo; 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.PlayerDieTypeOuterClass; import emu.grasscutter.net.proto.PlayerDieTypeOuterClass;
import emu.grasscutter.server.event.entity.EntityMoveEvent; import emu.grasscutter.server.event.entity.EntityMoveEvent;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
@Opcodes(PacketOpcodes.CombatInvocationsNotify) @Opcodes(PacketOpcodes.CombatInvocationsNotify)
public class HandlerCombatInvocationsNotify extends PacketHandler { public class HandlerCombatInvocationsNotify extends PacketHandler {
private float cachedLandingSpeed = 0; private float cachedLandingSpeed = 0;
private long cachedLandingTimeMillisecond = 0; private long cachedLandingTimeMillisecond = 0;
private boolean monitorLandingEvent = false; private boolean monitorLandingEvent = false;
@Override @Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
CombatInvocationsNotify notif = CombatInvocationsNotify.parseFrom(payload); CombatInvocationsNotify notif = CombatInvocationsNotify.parseFrom(payload);
for (CombatInvokeEntry entry : notif.getInvokeListList()) { for (CombatInvokeEntry entry : notif.getInvokeListList()) {
// Handle combat invoke // Handle combat invoke
switch (entry.getArgumentType()) { switch (entry.getArgumentType()) {
case COMBAT_TYPE_ARGUMENT_EVT_BEING_HIT -> { case COMBAT_TYPE_ARGUMENT_EVT_BEING_HIT -> {
EvtBeingHitInfo hitInfo = EvtBeingHitInfo.parseFrom(entry.getCombatData()); EvtBeingHitInfo hitInfo = EvtBeingHitInfo.parseFrom(entry.getCombatData());
AttackResult attackResult = hitInfo.getAttackResult(); AttackResult attackResult = hitInfo.getAttackResult();
Player player = session.getPlayer(); Player player = session.getPlayer();
// Check if the player is invulnerable. // Check if the player is invulnerable.
if (attackResult.getAttackerId() if (attackResult.getAttackerId()
!= player.getTeamManager().getCurrentAvatarEntity().getId() != player.getTeamManager().getCurrentAvatarEntity().getId()
&& player.getAbilityManager().isAbilityInvulnerable()) break; && player.getAbilityManager().isAbilityInvulnerable()) break;
// Handle damage // Handle damage
player.getAttackResults().add(attackResult); player.getAttackResults().add(attackResult);
player.getEnergyManager().handleAttackHit(hitInfo); player.getEnergyManager().handleAttackHit(hitInfo);
} }
case COMBAT_TYPE_ARGUMENT_ENTITY_MOVE -> { case COMBAT_TYPE_ARGUMENT_ENTITY_MOVE -> {
// Handle movement // Handle movement
EntityMoveInfo moveInfo = EntityMoveInfo.parseFrom(entry.getCombatData()); EntityMoveInfo moveInfo = EntityMoveInfo.parseFrom(entry.getCombatData());
GameEntity entity = session.getPlayer().getScene().getEntityById(moveInfo.getEntityId()); GameEntity entity = session.getPlayer().getScene().getEntityById(moveInfo.getEntityId());
if (entity != null) { if (entity != null) {
// Move player // Move player
MotionInfo motionInfo = moveInfo.getMotionInfo(); MotionInfo motionInfo = moveInfo.getMotionInfo();
MotionState motionState = motionInfo.getState(); MotionState motionState = motionInfo.getState();
// Call entity move event. // Call entity move event.
EntityMoveEvent event = EntityMoveEvent event =
new EntityMoveEvent( new EntityMoveEvent(
entity, entity,
new Position(motionInfo.getPos()), new Position(motionInfo.getPos()),
new Position(motionInfo.getRot()), new Position(motionInfo.getRot()),
motionState); motionState);
event.call(); event.call();
entity.move(event.getPosition(), event.getRotation()); entity.move(event.getPosition(), event.getRotation());
entity.setLastMoveSceneTimeMs(moveInfo.getSceneTime()); entity.setLastMoveSceneTimeMs(moveInfo.getSceneTime());
entity.setLastMoveReliableSeq(moveInfo.getReliableSeq()); entity.setLastMoveReliableSeq(moveInfo.getReliableSeq());
entity.setMotionState(motionState); entity.setMotionState(motionState);
session session
.getPlayer() .getPlayer()
.getStaminaManager() .getStaminaManager()
.handleCombatInvocationsNotify(session, moveInfo, entity); .handleCombatInvocationsNotify(session, moveInfo, entity);
// TODO: handle MOTION_FIGHT landing which has a different damage factor // TODO: handle MOTION_FIGHT landing which has a different damage factor
// Also, for plunge attacks, LAND_SPEED is always -30 and is not useful. // Also, for plunge attacks, LAND_SPEED is always -30 and is not useful.
// May need the height when starting plunge attack. // May need the height when starting plunge attack.
// MOTION_LAND_SPEED and MOTION_FALL_ON_GROUND arrive in different packets. // MOTION_LAND_SPEED and MOTION_FALL_ON_GROUND arrive in different packets.
// Cache land speed for later use. // Cache land speed for later use.
if (motionState == MotionState.MOTION_STATE_LAND_SPEED) { if (motionState == MotionState.MOTION_STATE_LAND_SPEED) {
cachedLandingSpeed = motionInfo.getSpeed().getY(); cachedLandingSpeed = motionInfo.getSpeed().getY();
cachedLandingTimeMillisecond = System.currentTimeMillis(); cachedLandingTimeMillisecond = System.currentTimeMillis();
monitorLandingEvent = true; monitorLandingEvent = true;
} }
if (monitorLandingEvent) { if (monitorLandingEvent) {
if (motionState == MotionState.MOTION_STATE_FALL_ON_GROUND) { if (motionState == MotionState.MOTION_STATE_FALL_ON_GROUND) {
monitorLandingEvent = false; monitorLandingEvent = false;
handleFallOnGround(session, entity, motionState); handleFallOnGround(session, entity, motionState);
} }
} }
// MOTION_STATE_NOTIFY = Dont send to other players // MOTION_STATE_NOTIFY = Dont send to other players
if (motionState == MotionState.MOTION_STATE_NOTIFY) { if (motionState == MotionState.MOTION_STATE_NOTIFY) {
continue; continue;
} }
} }
} }
case COMBAT_TYPE_ARGUMENT_ANIMATOR_PARAMETER_CHANGED -> { case COMBAT_TYPE_ARGUMENT_ANIMATOR_PARAMETER_CHANGED -> {
EvtAnimatorParameterInfo paramInfo = EvtAnimatorParameterInfo paramInfo =
EvtAnimatorParameterInfo.parseFrom(entry.getCombatData()); EvtAnimatorParameterInfo.parseFrom(entry.getCombatData());
if (paramInfo.getIsServerCache()) { if (paramInfo.getIsServerCache()) {
paramInfo = paramInfo.toBuilder().setIsServerCache(false).build(); paramInfo = paramInfo.toBuilder().setIsServerCache(false).build();
entry = entry.toBuilder().setCombatData(paramInfo.toByteString()).build(); entry = entry.toBuilder().setCombatData(paramInfo.toByteString()).build();
} }
} }
default -> {} default -> {}
} }
session.getPlayer().getCombatInvokeHandler().addEntry(entry.getForwardType(), entry); session.getPlayer().getCombatInvokeHandler().addEntry(entry.getForwardType(), entry);
} }
} }
private void handleFallOnGround(GameSession session, GameEntity entity, MotionState motionState) { private void handleFallOnGround(GameSession session, GameEntity entity, MotionState motionState) {
if (session.getPlayer().isInGodMode()) { 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
// FALL_ON_GROUND) they will die // FALL_ON_GROUND) they will die
// if they talk to an NPC (this is when the client sends a FALL_ON_GROUND) without jumping // if they talk to an NPC (this is when the client sends a FALL_ON_GROUND) without jumping
// again. // again.
// A dirty patch: if not received immediately after MOTION_LAND_SPEED, discard this packet. // A dirty patch: if not received immediately after MOTION_LAND_SPEED, discard this packet.
// 200ms seems to be a reasonable delay. // 200ms seems to be a reasonable delay.
int maxDelay = 200; int maxDelay = 200;
long actualDelay = System.currentTimeMillis() - cachedLandingTimeMillisecond; long actualDelay = System.currentTimeMillis() - cachedLandingTimeMillisecond;
Grasscutter.getLogger() Grasscutter.getLogger()
.trace( .trace(
"MOTION_FALL_ON_GROUND received after " "MOTION_FALL_ON_GROUND received after "
+ actualDelay + actualDelay
+ "/" + "/"
+ maxDelay + maxDelay
+ "ms." + "ms."
+ (actualDelay > maxDelay ? " Discard" : "")); + (actualDelay > maxDelay ? " Discard" : ""));
if (actualDelay > maxDelay) { if (actualDelay > maxDelay) {
return; return;
} }
float currentHP = entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP); float currentHP = entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
float maxHP = entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); float maxHP = entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
float damageFactor = 0; float damageFactor = 0;
if (cachedLandingSpeed < -23.5) { if (cachedLandingSpeed < -23.5) {
damageFactor = 0.33f; damageFactor = 0.33f;
} }
if (cachedLandingSpeed < -25) { if (cachedLandingSpeed < -25) {
damageFactor = 0.5f; damageFactor = 0.5f;
} }
if (cachedLandingSpeed < -26.5) { if (cachedLandingSpeed < -26.5) {
damageFactor = 0.66f; damageFactor = 0.66f;
} }
if (cachedLandingSpeed < -28) { if (cachedLandingSpeed < -28) {
damageFactor = 1f; damageFactor = 1f;
} }
float damage = maxHP * damageFactor; float damage = maxHP * damageFactor;
float newHP = currentHP - damage; float newHP = currentHP - damage;
if (newHP < 0) { if (newHP < 0) {
newHP = 0; newHP = 0;
} }
if (damageFactor > 0) { if (damageFactor > 0) {
Grasscutter.getLogger() Grasscutter.getLogger()
.debug( .debug(
currentHP currentHP
+ "/" + "/"
+ maxHP + maxHP
+ "\tLandingSpeed: " + "\tLandingSpeed: "
+ cachedLandingSpeed + cachedLandingSpeed
+ "\tDamageFactor: " + "\tDamageFactor: "
+ damageFactor + damageFactor
+ "\tDamage: " + "\tDamage: "
+ damage + damage
+ "\tNewHP: " + "\tNewHP: "
+ newHP); + newHP);
} else { } else {
Grasscutter.getLogger().trace(currentHP + "/" + maxHP + "\tLandingSpeed: 0\tNo damage"); Grasscutter.getLogger().trace(currentHP + "/" + maxHP + "\tLandingSpeed: 0\tNo damage");
} }
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP); entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
entity entity
.getWorld() .getWorld()
.broadcastPacket( .broadcastPacket(
new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
if (newHP == 0) { if (newHP == 0) {
session session
.getPlayer() .getPlayer()
.getStaminaManager() .getStaminaManager()
.killAvatar(session, entity, PlayerDieTypeOuterClass.PlayerDieType.PLAYER_DIE_TYPE_FALL); .killAvatar(session, entity, PlayerDieTypeOuterClass.PlayerDieType.PLAYER_DIE_TYPE_FALL);
} }
cachedLandingSpeed = 0; cachedLandingSpeed = 0;
} }
} }

View File

@ -1,29 +1,29 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.quest.enums.QuestContent; 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;
import emu.grasscutter.net.proto.EvtDoSkillSuccNotifyOuterClass.EvtDoSkillSuccNotify; import emu.grasscutter.net.proto.EvtDoSkillSuccNotifyOuterClass.EvtDoSkillSuccNotify;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.EvtDoSkillSuccNotify) @Opcodes(PacketOpcodes.EvtDoSkillSuccNotify)
public class HandlerEvtDoSkillSuccNotify extends PacketHandler { public class HandlerEvtDoSkillSuccNotify 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 {
EvtDoSkillSuccNotify notify = EvtDoSkillSuccNotify.parseFrom(payload); EvtDoSkillSuccNotify notify = EvtDoSkillSuccNotify.parseFrom(payload);
var player = session.getPlayer(); var player = session.getPlayer();
int skillId = notify.getSkillId(); int skillId = notify.getSkillId();
int casterId = notify.getCasterId(); int casterId = notify.getCasterId();
// Call skill perform in the player's ability manager. // Call skill perform in the player's ability manager.
player.getAbilityManager().onSkillStart(session.getPlayer(), skillId, casterId); player.getAbilityManager().onSkillStart(session.getPlayer(), skillId, casterId);
// 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().queueEvent(QuestContent.QUEST_CONTENT_SKILL, skillId, 0); player.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_SKILL, skillId, 0);
} }
} }

View File

@ -1,29 +1,29 @@
package emu.grasscutter.server.packet.recv; 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.player.Player; import emu.grasscutter.game.player.Player;
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.ExecuteGadgetLuaReqOuterClass.ExecuteGadgetLuaReq; import emu.grasscutter.net.proto.ExecuteGadgetLuaReqOuterClass.ExecuteGadgetLuaReq;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketExecuteGadgetLuaRsp; import emu.grasscutter.server.packet.send.PacketExecuteGadgetLuaRsp;
@Opcodes(PacketOpcodes.ExecuteGadgetLuaReq) @Opcodes(PacketOpcodes.ExecuteGadgetLuaReq)
public class HandlerExecuteGadgetLuaReq extends PacketHandler { public class HandlerExecuteGadgetLuaReq 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 {
ExecuteGadgetLuaReq req = ExecuteGadgetLuaReq.parseFrom(payload); ExecuteGadgetLuaReq req = ExecuteGadgetLuaReq.parseFrom(payload);
Player player = session.getPlayer(); Player player = session.getPlayer();
GameEntity entity = player.getScene().getEntities().get(req.getSourceEntityId()); GameEntity entity = player.getScene().getEntities().get(req.getSourceEntityId());
int result = 1; int result = 1;
if (entity instanceof EntityGadget gadget) if (entity instanceof EntityGadget gadget)
result = gadget.onClientExecuteRequest(req.getParam1(), req.getParam2(), req.getParam3()); result = gadget.onClientExecuteRequest(req.getParam1(), req.getParam2(), req.getParam3());
player.sendPacket(new PacketExecuteGadgetLuaRsp(result)); player.sendPacket(new PacketExecuteGadgetLuaRsp(result));
} }
} }

View File

@ -1,23 +1,23 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.quest.enums.QuestContent; 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;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq; import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.GadgetInteractReq) @Opcodes(PacketOpcodes.GadgetInteractReq)
public class HandlerGadgetInteractReq extends PacketHandler { public class HandlerGadgetInteractReq 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 {
GadgetInteractReq req = GadgetInteractReq.parseFrom(payload); GadgetInteractReq req = GadgetInteractReq.parseFrom(payload);
session session
.getPlayer() .getPlayer()
.getQuestManager() .getQuestManager()
.queueEvent(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

@ -1,17 +1,17 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
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.GetAllMailNotifyOuterClass.GetAllMailNotify; import emu.grasscutter.net.proto.GetAllMailNotifyOuterClass.GetAllMailNotify;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketGetAllMailResultNotify; import emu.grasscutter.server.packet.send.PacketGetAllMailResultNotify;
@Opcodes(PacketOpcodes.GetAllMailNotify) @Opcodes(PacketOpcodes.GetAllMailNotify)
public final class HandlerGetAllMailNotify extends PacketHandler { public final class HandlerGetAllMailNotify 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 = GetAllMailNotify.parseFrom(payload); var req = GetAllMailNotify.parseFrom(payload);
session.send(new PacketGetAllMailResultNotify(session.getPlayer(), req.getIsCollected())); session.send(new PacketGetAllMailResultNotify(session.getPlayer(), req.getIsCollected()));
} }
} }

View File

@ -1,19 +1,19 @@
//package emu.grasscutter.server.packet.recv; // package emu.grasscutter.server.packet.recv;
// //
//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.server.game.GameSession; // import emu.grasscutter.server.game.GameSession;
//import emu.grasscutter.server.packet.send.PacketHomeUnknown2Rsp; // import emu.grasscutter.server.packet.send.PacketHomeUnknown2Rsp;
// //
//@Opcodes(PacketOpcodes.Unk2700_ACILPONNGGK_ClientReq) // @Opcodes(PacketOpcodes.Unk2700_ACILPONNGGK_ClientReq)
//public class HandlerHomeUnknown2Req extends PacketHandler { // public class HandlerHomeUnknown2Req 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 {
// /* // /*
// * This packet is about the edit mode // * This packet is about the edit mode
// */ // */
// session.send(new PacketHomeUnknown2Rsp()); // session.send(new PacketHomeUnknown2Rsp());
// } // }
//} // }

View File

@ -1,66 +1,75 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.activity.musicgame.MusicGameActivityHandler; import emu.grasscutter.game.activity.musicgame.MusicGameActivityHandler;
import emu.grasscutter.game.activity.musicgame.MusicGamePlayerData; import emu.grasscutter.game.activity.musicgame.MusicGamePlayerData;
import emu.grasscutter.game.props.ActivityType; import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.game.props.WatcherTriggerType; 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.MusicGameSettleReq; import emu.grasscutter.net.proto.MusicGameSettleReqOuterClass.MusicGameSettleReq;
import emu.grasscutter.net.proto.RetcodeOuterClass; 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; 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 {
val req = MusicGameSettleReq.parseFrom(payload); val req = MusicGameSettleReq.parseFrom(payload);
val activityManager = session.getPlayer().getActivityManager(); val activityManager = session.getPlayer().getActivityManager();
val playerDataOpt = activityManager.getPlayerActivityDataByActivityType(ActivityType.NEW_ACTIVITY_MUSIC_GAME); val playerDataOpt =
if (playerDataOpt.isEmpty()) { activityManager.getPlayerActivityDataByActivityType(ActivityType.NEW_ACTIVITY_MUSIC_GAME);
session.send(new PacketMusicGameSettleRsp(RetcodeOuterClass.Retcode.RET_MUSIC_GAME_LEVEL_CONFIG_NOT_FOUND, req)); if (playerDataOpt.isEmpty()) {
return; session.send(
} new PacketMusicGameSettleRsp(
RetcodeOuterClass.Retcode.RET_MUSIC_GAME_LEVEL_CONFIG_NOT_FOUND, req));
val playerData = playerDataOpt.get(); return;
val handler = (MusicGameActivityHandler) playerData.getActivityHandler(); }
boolean isNewRecord = false;
val playerData = playerDataOpt.get();
// check if custom beatmap val handler = (MusicGameActivityHandler) playerData.getActivityHandler();
if (req.getUgcGuid() == 0) { boolean isNewRecord = false;
session.getPlayer().getActivityManager().triggerWatcher(
WatcherTriggerType.TRIGGER_FLEUR_FAIR_MUSIC_GAME_REACH_SCORE, // check if custom beatmap
String.valueOf(req.getMusicBasicId()), if (req.getUgcGuid() == 0) {
String.valueOf(req.getScore()) session
); .getPlayer()
.getActivityManager()
isNewRecord = handler.setMusicGameRecord(playerData, .triggerWatcher(
MusicGamePlayerData.MusicGameRecord.of() WatcherTriggerType.TRIGGER_FLEUR_FAIR_MUSIC_GAME_REACH_SCORE,
.musicId(req.getMusicBasicId()) String.valueOf(req.getMusicBasicId()),
.maxCombo(req.getMaxCombo()) String.valueOf(req.getScore()));
.maxScore(req.getScore())
.build()); isNewRecord =
handler.setMusicGameRecord(
// update activity info playerData,
session.send(new PacketActivityInfoNotify(handler.toProto(playerData, activityManager.getConditionExecutor()))); MusicGamePlayerData.MusicGameRecord.of()
} else { .musicId(req.getMusicBasicId())
handler.setMusicGameCustomBeatmapRecord(playerData, .maxCombo(req.getMaxCombo())
MusicGamePlayerData.CustomBeatmapRecord.of() .maxScore(req.getScore())
.musicShareId(req.getUgcGuid()) .build());
.score(req.getMaxCombo())
.settle(req.getIsSaveScore()) // update activity info
.build()); session.send(
} new PacketActivityInfoNotify(
handler.toProto(playerData, activityManager.getConditionExecutor())));
} else {
session.send(new PacketMusicGameSettleRsp(req.getMusicBasicId(), req.getUgcGuid(), isNewRecord)); handler.setMusicGameCustomBeatmapRecord(
} playerData,
MusicGamePlayerData.CustomBeatmapRecord.of()
} .musicShareId(req.getUgcGuid())
.score(req.getMaxCombo())
.settle(req.getIsSaveScore())
.build());
}
session.send(
new PacketMusicGameSettleRsp(req.getMusicBasicId(), req.getUgcGuid(), isNewRecord));
}
}

View File

@ -1,67 +1,67 @@
package emu.grasscutter.server.packet.recv; 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.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;
import emu.grasscutter.net.proto.NpcTalkReqOuterClass.NpcTalkReq; import emu.grasscutter.net.proto.NpcTalkReqOuterClass.NpcTalkReq;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketNpcTalkRsp; import emu.grasscutter.server.packet.send.PacketNpcTalkRsp;
@Opcodes(PacketOpcodes.NpcTalkReq) @Opcodes(PacketOpcodes.NpcTalkReq)
public class HandlerNpcTalkReq extends PacketHandler { public class HandlerNpcTalkReq 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 {
NpcTalkReq req = NpcTalkReq.parseFrom(payload); NpcTalkReq req = NpcTalkReq.parseFrom(payload);
// Check if mainQuest exists // Check if mainQuest exists
// remove last 2 digits to get a mainQuestId // remove last 2 digits to get a mainQuestId
int talkId = req.getTalkId(); int talkId = req.getTalkId();
int mainQuestId = talkId / 100; int mainQuestId = talkId / 100;
MainQuestData mainQuestData = GameData.getMainQuestDataMap().get(mainQuestId); MainQuestData mainQuestData = GameData.getMainQuestDataMap().get(mainQuestId);
if (mainQuestData != null) { if (mainQuestData != null) {
// This talk is associated with a quest. Handle it. // This talk is associated with a quest. Handle it.
// If the quest has no talk data defined on it, create one. // If the quest has no talk data defined on it, create one.
TalkData talkForQuest = new TalkData(talkId, ""); TalkData talkForQuest = new TalkData(talkId, "");
if (mainQuestData.getTalks() != null) { if (mainQuestData.getTalks() != null) {
var talks = mainQuestData.getTalks().stream().filter(p -> p.getId() == talkId).toList(); var talks = mainQuestData.getTalks().stream().filter(p -> p.getId() == talkId).toList();
if (talks.size() > 0) { if (talks.size() > 0) {
talkForQuest = talks.get(0); talkForQuest = talks.get(0);
} }
} }
// Add to the list of done talks for this quest. // Add to the list of done talks for this quest.
var mainQuest = session.getPlayer().getQuestManager().getMainQuestById(mainQuestId); var mainQuest = session.getPlayer().getQuestManager().getMainQuestById(mainQuestId);
if (mainQuest != null) { if (mainQuest != null) {
session session
.getPlayer() .getPlayer()
.getQuestManager() .getQuestManager()
.getMainQuestById(mainQuestId) .getMainQuestById(mainQuestId)
.getTalks() .getTalks()
.put(talkId, talkForQuest); .put(talkId, talkForQuest);
} }
// Fire quest triggers. // Fire quest triggers.
session session
.getPlayer() .getPlayer()
.getQuestManager() .getQuestManager()
.queueEvent( .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()
.queueEvent(QuestContent.QUEST_CONTENT_COMPLETE_TALK, req.getTalkId(), 0); .queueEvent(QuestContent.QUEST_CONTENT_COMPLETE_TALK, req.getTalkId(), 0);
session session
.getPlayer() .getPlayer()
.getQuestManager() .getQuestManager()
.queueEvent(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,25 +1,25 @@
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.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;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketPostEnterSceneRsp; import emu.grasscutter.server.packet.send.PacketPostEnterSceneRsp;
@Opcodes(PacketOpcodes.PostEnterSceneReq) @Opcodes(PacketOpcodes.PostEnterSceneReq)
public class HandlerPostEnterSceneReq extends PacketHandler { public class HandlerPostEnterSceneReq 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 {
if (session.getPlayer().getScene().getSceneType() == SceneType.SCENE_ROOM) { if (session.getPlayer().getScene().getSceneType() == SceneType.SCENE_ROOM) {
session session
.getPlayer() .getPlayer()
.getQuestManager() .getQuestManager()
.queueEvent(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

@ -1,16 +1,16 @@
//package emu.grasscutter.server.packet.recv; // package emu.grasscutter.server.packet.recv;
// //
//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.RemoveCustomTeamReqOuterClass.RemoveCustomTeamReq; // import emu.grasscutter.net.proto.RemoveCustomTeamReqOuterClass.RemoveCustomTeamReq;
//import emu.grasscutter.server.game.GameSession; // import emu.grasscutter.server.game.GameSession;
// //
//@Opcodes(PacketOpcodes.RemoveCustomTeamReq) // @Opcodes(PacketOpcodes.RemoveCustomTeamReq)
//public class HandlerRemoveCustomTeamReq extends PacketHandler { // public class HandlerRemoveCustomTeamReq 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 {
// RemoveCustomTeamReq req = RemoveCustomTeamReq.parseFrom(payload); // RemoveCustomTeamReq req = RemoveCustomTeamReq.parseFrom(payload);
// session.getPlayer().getTeamManager().removeCustomTeam(req.getId()); // session.getPlayer().getTeamManager().removeCustomTeam(req.getId());
// } // }
//} // }

View File

@ -1,97 +1,102 @@
//package emu.grasscutter.server.packet.recv; // package emu.grasscutter.server.packet.recv;
// //
//import emu.grasscutter.database.DatabaseHelper; // import emu.grasscutter.database.DatabaseHelper;
//import emu.grasscutter.game.activity.musicgame.MusicGameActivityHandler; // import emu.grasscutter.game.activity.musicgame.MusicGameActivityHandler;
//import emu.grasscutter.game.activity.musicgame.MusicGameBeatmap; // import emu.grasscutter.game.activity.musicgame.MusicGameBeatmap;
//import emu.grasscutter.game.activity.musicgame.MusicGamePlayerData; // import emu.grasscutter.game.activity.musicgame.MusicGamePlayerData;
//import emu.grasscutter.game.props.ActivityType; // import emu.grasscutter.game.props.ActivityType;
//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.RetcodeOuterClass; // import emu.grasscutter.net.proto.RetcodeOuterClass;
//import emu.grasscutter.net.proto.SaveUgcReqOuterClass; // import emu.grasscutter.net.proto.SaveUgcReqOuterClass;
//import emu.grasscutter.net.proto.UgcTypeOuterClass; // import emu.grasscutter.net.proto.UgcTypeOuterClass;
//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.PacketMusicGameCreateBeatmapRsp; // import emu.grasscutter.server.packet.send.PacketMusicGameCreateBeatmapRsp;
//import emu.grasscutter.utils.Utils; // import emu.grasscutter.utils.Utils;
//import java.util.Objects; // import java.util.Objects;
//import lombok.val; // import lombok.val;
// //
//@Opcodes(PacketOpcodes.SaveUgcReq) // @Opcodes(PacketOpcodes.SaveUgcReq)
//public class HandlerSaveUgcReq extends PacketHandler { // public class HandlerSaveUgcReq 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 {
// val req = SaveUgcReqOuterClass.SaveUgcReq.parseFrom(payload); // val req = SaveUgcReqOuterClass.SaveUgcReq.parseFrom(payload);
// //
// // We only support music game user generated content // // We only support music game user generated content
// if (req.getUgcType() != UgcTypeOuterClass.UgcType.UGC_TYPE_MUSIC_GAME) { // if (req.getUgcType() != UgcTypeOuterClass.UgcType.UGC_TYPE_MUSIC_GAME) {
// session.send( // session.send(
// new PacketMusicGameCreateBeatmapRsp( // new PacketMusicGameCreateBeatmapRsp(
// RetcodeOuterClass.Retcode.RET_UGC_DISABLED, req.getUgcType())); // RetcodeOuterClass.Retcode.RET_UGC_DISABLED, req.getUgcType()));
// return; // return;
// } // }
// val briefInfo = req.getMusicBriefInfo(); // val briefInfo = req.getMusicBriefInfo();
// //
// val musicGameBeatmap = // val musicGameBeatmap =
// MusicGameBeatmap.of() // MusicGameBeatmap.of()
// .musicId(briefInfo.getMusicId()) // .musicId(briefInfo.getMusicId())
// .musicNoteCount(briefInfo.getNoteCount()) // .musicNoteCount(briefInfo.getNoteCount())
// .savePosition(briefInfo.getSaveIdx()) // .savePosition(briefInfo.getSaveIdx())
// .savePageType(briefInfo.getSavePageType()) // .savePageType(briefInfo.getSavePageType())
// .version(briefInfo.getVersion()) // .version(briefInfo.getVersion())
// .afterNoteList(briefInfo.getAfterNoteListList()) // .afterNoteList(briefInfo.getAfterNoteListList())
// .beforeNoteList(briefInfo.getBeforeNoteListList()) // .beforeNoteList(briefInfo.getBeforeNoteListList())
// .timeLineEditTime(briefInfo.getTimeLineEditTime()) // .timeLineEditTime(briefInfo.getTimeLineEditTime())
// .publishTime(briefInfo.getPublishTime()) // .publishTime(briefInfo.getPublishTime())
// .realTimeEditTime(briefInfo.getRealTimeEditTime()) // .realTimeEditTime(briefInfo.getRealTimeEditTime())
// .maxScore(briefInfo.getMaxScore()) // .maxScore(briefInfo.getMaxScore())
// .authorUid(session.getPlayer().getUid()) // .authorUid(session.getPlayer().getUid())
// .beatmap(MusicGameBeatmap.parse(req.getMusicRecord().getMusicTrackListList())) //
// .createTime(Utils.getCurrentSeconds()) // .beatmap(MusicGameBeatmap.parse(req.getMusicRecord().getMusicTrackListList()))
// .build(); // .createTime(Utils.getCurrentSeconds())
// // .build();
// musicGameBeatmap.save(); //
// // musicGameBeatmap.save();
// val playerData = //
// session // val playerData =
// .getPlayer() // session
// .getActivityManager() // .getPlayer()
// .getPlayerActivityDataByActivityType(ActivityType.NEW_ACTIVITY_MUSIC_GAME); // .getActivityManager()
// if (playerData.isEmpty()) { //
// session.send( // .getPlayerActivityDataByActivityType(ActivityType.NEW_ACTIVITY_MUSIC_GAME);
// new PacketMusicGameCreateBeatmapRsp( // if (playerData.isEmpty()) {
// RetcodeOuterClass.Retcode.RET_UGC_DATA_NOT_FOUND, req.getUgcType())); // session.send(
// return; // new PacketMusicGameCreateBeatmapRsp(
// } // RetcodeOuterClass.Retcode.RET_UGC_DATA_NOT_FOUND, req.getUgcType()));
// // return;
// val handler = (MusicGameActivityHandler) playerData.get().getActivityHandler(); // }
// val musicGamePlayerData = handler.getMusicGamePlayerData(playerData.get()); //
// // val handler = (MusicGameActivityHandler) playerData.get().getActivityHandler();
// val oldBeatmap = // val musicGamePlayerData = handler.getMusicGamePlayerData(playerData.get());
// musicGamePlayerData.getPersonalCustomBeatmapRecord().values().stream() //
// .map(MusicGamePlayerData.CustomBeatmapRecord::getMusicShareId) // val oldBeatmap =
// .map(DatabaseHelper::getMusicGameBeatmap) // musicGamePlayerData.getPersonalCustomBeatmapRecord().values().stream()
// .filter(Objects::nonNull) // .map(MusicGamePlayerData.CustomBeatmapRecord::getMusicShareId)
// .filter(item -> item.getAuthorUid() == session.getPlayer().getUid()) // .map(DatabaseHelper::getMusicGameBeatmap)
// .filter(item -> item.getMusicId() == req.getMusicBriefInfo().getMusicId()) // .filter(Objects::nonNull)
// .filter(item -> item.getSavePosition() == req.getMusicBriefInfo().getSaveIdx()) // .filter(item -> item.getAuthorUid() == session.getPlayer().getUid())
// .findFirst(); // .filter(item -> item.getMusicId() == req.getMusicBriefInfo().getMusicId())
// // .filter(item -> item.getSavePosition() ==
// // delete old beatmap for player // req.getMusicBriefInfo().getSaveIdx())
// // the old beatmap is still in database so that others can still play. // .findFirst();
// oldBeatmap.ifPresent(i -> handler.removePersonalBeatmap(playerData.get(), i)); //
// // // delete old beatmap for player
// // link this beatmap to player's personal data // // the old beatmap is still in database so that others can still play.
// handler.addPersonalBeatmap(playerData.get(), musicGameBeatmap); // oldBeatmap.ifPresent(i -> handler.removePersonalBeatmap(playerData.get(), i));
// //
// session.send( // // link this beatmap to player's personal data
// new PacketActivityInfoNotify( // handler.addPersonalBeatmap(playerData.get(), musicGameBeatmap);
// handler.toProto( //
// playerData.get(), // session.send(
// session.getPlayer().getActivityManager().getConditionExecutor()))); // new PacketActivityInfoNotify(
// session.send( // handler.toProto(
// new PacketMusicGameCreateBeatmapRsp(musicGameBeatmap.getMusicShareId(), req.getUgcType())); // playerData.get(),
// } //
//} // session.getPlayer().getActivityManager().getConditionExecutor())));
// session.send(
// new PacketMusicGameCreateBeatmapRsp(musicGameBeatmap.getMusicShareId(),
// req.getUgcType()));
// }
// }

View File

@ -1,39 +1,49 @@
package emu.grasscutter.server.packet.recv; 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.game.quest.enums.QuestContent;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.proto.SelectWorktopOptionReqOuterClass.SelectWorktopOptionReq; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.net.proto.SelectWorktopOptionReqOuterClass.SelectWorktopOptionReq;
import emu.grasscutter.scripts.data.ScriptArgs; import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.scripts.data.ScriptArgs;
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;
@Opcodes(PacketOpcodes.SelectWorktopOptionReq) @Opcodes(PacketOpcodes.SelectWorktopOptionReq)
public class HandlerSelectWorktopOptionReq extends PacketHandler { public class HandlerSelectWorktopOptionReq 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 {
SelectWorktopOptionReq req = SelectWorktopOptionReq.parseFrom(payload); SelectWorktopOptionReq req = SelectWorktopOptionReq.parseFrom(payload);
try { try {
GameEntity entity = session.getPlayer().getScene().getEntityById(req.getGadgetEntityId()); GameEntity entity = session.getPlayer().getScene().getEntityById(req.getGadgetEntityId());
if (!(entity instanceof EntityGadget)) { if (!(entity instanceof EntityGadget)) {
return; return;
} }
session.getPlayer().getScene().selectWorktopOptionWith(req); session.getPlayer().getScene().selectWorktopOptionWith(req);
session.getPlayer().getScene().getScriptManager().callEvent( session
new ScriptArgs(entity.getGroupId(), EventType.EVENT_SELECT_OPTION, entity.getConfigId(), req.getOptionId()) .getPlayer()
); .getScene()
session.getPlayer().getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_WORKTOP_SELECT, entity.getConfigId(), req.getOptionId()); .getScriptManager()
} finally { .callEvent(
// Always send packet new ScriptArgs(
session.send(new PacketSelectWorktopOptionRsp(req.getGadgetEntityId(), req.getOptionId())); entity.getGroupId(),
} EventType.EVENT_SELECT_OPTION,
} entity.getConfigId(),
req.getOptionId()));
} session
.getPlayer()
.getQuestManager()
.queueEvent(
QuestContent.QUEST_CONTENT_WORKTOP_SELECT, entity.getConfigId(), req.getOptionId());
} finally {
// Always send packet
session.send(new PacketSelectWorktopOptionRsp(req.getGadgetEntityId(), req.getOptionId()));
}
}
}

View File

@ -1,21 +1,21 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
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.SkipPlayerGameTimeReqOuterClass; import emu.grasscutter.net.proto.SkipPlayerGameTimeReqOuterClass;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketPlayerGameTimeNotify; import emu.grasscutter.server.packet.send.PacketPlayerGameTimeNotify;
import emu.grasscutter.server.packet.send.PacketSkipPlayerGameTimeRsp; import emu.grasscutter.server.packet.send.PacketSkipPlayerGameTimeRsp;
@Opcodes(PacketOpcodes.SkipPlayerGameTimeReq) @Opcodes(PacketOpcodes.SkipPlayerGameTimeReq)
public class HandlerSkipPlayerGameTimeReq extends PacketHandler { public class HandlerSkipPlayerGameTimeReq 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 = SkipPlayerGameTimeReqOuterClass.SkipPlayerGameTimeReq.parseFrom(payload); var req = SkipPlayerGameTimeReqOuterClass.SkipPlayerGameTimeReq.parseFrom(payload);
var player = session.getPlayer(); var player = session.getPlayer();
player.updatePlayerGameTime(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,27 +1,28 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.gadget.platform.AbilityRoute; 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.UpdateAbilityCreatedMovingPlatformNotify; import emu.grasscutter.net.proto.UpdateAbilityCreatedMovingPlatformNotifyOuterClass.UpdateAbilityCreatedMovingPlatformNotify;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
@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 = UpdateAbilityCreatedMovingPlatformNotify.parseFrom(payload); var notify = UpdateAbilityCreatedMovingPlatformNotify.parseFrom(payload);
var entity = session.getPlayer().getScene().getEntityById(notify.getEntityId()); var entity = session.getPlayer().getScene().getEntityById(notify.getEntityId());
if (!(entity instanceof EntityGadget entityGadget) || !(entityGadget.getRouteConfig() instanceof AbilityRoute)) { if (!(entity instanceof EntityGadget entityGadget)
return; || !(entityGadget.getRouteConfig() instanceof AbilityRoute)) {
} return;
}
switch (notify.getOpType()) {
case OP_TYPE_ACTIVATE -> entityGadget.startPlatform(); switch (notify.getOpType()) {
case OP_TYPE_DEACTIVATE -> entityGadget.stopPlatform(); case OP_TYPE_ACTIVATE -> entityGadget.startPlatform();
} case OP_TYPE_DEACTIVATE -> entityGadget.stopPlatform();
} }
} }
}

View File

@ -1,20 +1,21 @@
//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;
//import emu.grasscutter.net.proto.AddCustomTeamRspOuterClass.AddCustomTeamRsp; // import emu.grasscutter.net.proto.AddCustomTeamRspOuterClass.AddCustomTeamRsp;
//import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; // import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
// //
//public class PacketAddCustomTeamRsp extends BasePacket { // public class PacketAddCustomTeamRsp extends BasePacket {
// public PacketAddCustomTeamRsp(Retcode retcode) { // public PacketAddCustomTeamRsp(Retcode retcode) {
// super(PacketOpcodes.AddCustomTeamRsp); // super(PacketOpcodes.AddCustomTeamRsp);
// //
// AddCustomTeamRsp proto = AddCustomTeamRsp.newBuilder().setRetcode(retcode.getNumber()).build(); // AddCustomTeamRsp proto =
// // AddCustomTeamRsp.newBuilder().setRetcode(retcode.getNumber()).build();
// this.setData(proto); //
// } // this.setData(proto);
// // }
// public PacketAddCustomTeamRsp() { //
// this(Retcode.RET_SUCC); // public PacketAddCustomTeamRsp() {
// } // this(Retcode.RET_SUCC);
//} // }
// }

View File

@ -1,61 +1,61 @@
//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;
//import emu.grasscutter.net.proto.BeginCameraSceneLookNotifyOuterClass.BeginCameraSceneLookNotify; // import emu.grasscutter.net.proto.BeginCameraSceneLookNotifyOuterClass.BeginCameraSceneLookNotify;
//import emu.grasscutter.utils.Position; // import emu.grasscutter.utils.Position;
//import java.util.ArrayList; // import java.util.ArrayList;
//import java.util.Collection; // import java.util.Collection;
//import lombok.Data; // import lombok.Data;
//import lombok.NoArgsConstructor; // import lombok.NoArgsConstructor;
// //
//public class PacketBeginCameraSceneLookNotify extends BasePacket { // public class PacketBeginCameraSceneLookNotify extends BasePacket {
// //
// public PacketBeginCameraSceneLookNotify(CameraSceneLookNotify parameters) { // public PacketBeginCameraSceneLookNotify(CameraSceneLookNotify parameters) {
// super(PacketOpcodes.BeginCameraSceneLookNotify); // super(PacketOpcodes.BeginCameraSceneLookNotify);
// var builder = // var builder =
// BeginCameraSceneLookNotify.newBuilder() // BeginCameraSceneLookNotify.newBuilder()
// .setLookPos(parameters.lookPos.toProto()) // .setLookPos(parameters.lookPos.toProto())
// .setFollowPos(parameters.followPos.toProto()) // .setFollowPos(parameters.followPos.toProto())
// .setDuration(parameters.duration) // .setDuration(parameters.duration)
// .setIsAllowInput(parameters.isAllowInput) // .setIsAllowInput(parameters.isAllowInput)
// .setIsSetFollowPos(parameters.setFollowPos) // .setIsSetFollowPos(parameters.setFollowPos)
// .setIsSetScreenXy(parameters.isScreenXY) // .setIsSetScreenXy(parameters.isScreenXY)
// .setIsRecoverKeepCurrent(parameters.recoverKeepCurrent) // .setIsRecoverKeepCurrent(parameters.recoverKeepCurrent)
// .setIsChangePlayMode(parameters.isChangePlayMode) // .setIsChangePlayMode(parameters.isChangePlayMode)
// .setScreenY(parameters.screenY) // .setScreenY(parameters.screenY)
// .setScreenX(parameters.screenX) // .setScreenX(parameters.screenX)
// .setIsForce(parameters.isForce) // .setIsForce(parameters.isForce)
// .setIsForce(parameters.isForceWalk) // .setIsForce(parameters.isForceWalk)
// .setEntityId(parameters.entityId) // .setEntityId(parameters.entityId)
// .addAllOtherParams(parameters.otherParams); // .addAllOtherParams(parameters.otherParams);
// this.setData(builder); // this.setData(builder);
// } // }
// //
// // TODO check default values // // TODO check default values
// // todo find missing field usages: // // todo find missing field usages:
// // enum Unk2700_HIAKNNCKHJB (Unk2700_LNCHDDOOECD) // // enum Unk2700_HIAKNNCKHJB (Unk2700_LNCHDDOOECD)
// // Unk3000_MNLLCJMPMNH (uint32) // // Unk3000_MNLLCJMPMNH (uint32)
// // Unk2700_DHAHEKOGHBJ (float) // // Unk2700_DHAHEKOGHBJ (float)
// // Unk3000_IEFIKMHCKDH (uint32) // // Unk3000_IEFIKMHCKDH (uint32)
// // Unk3000_OGCLMFFADBD (float) // // Unk3000_OGCLMFFADBD (float)
// //
// @Data // @Data
// @NoArgsConstructor // @NoArgsConstructor
// public static class CameraSceneLookNotify { // public static class CameraSceneLookNotify {
// Position lookPos = new Position(); // Position lookPos = new Position();
// Position followPos = new Position(); // Position followPos = new Position();
// float duration = 0.0f; // float duration = 0.0f;
// boolean isAllowInput = true; // boolean isAllowInput = true;
// boolean setFollowPos = false; // boolean setFollowPos = false;
// boolean isScreenXY = false; // boolean isScreenXY = false;
// boolean recoverKeepCurrent = true; // boolean recoverKeepCurrent = true;
// boolean isForceWalk = false; // boolean isForceWalk = false;
// boolean isForce = false; // boolean isForce = false;
// boolean isChangePlayMode = false; // boolean isChangePlayMode = false;
// float screenY = 0.0f; // float screenY = 0.0f;
// float screenX = 0.0f; // float screenX = 0.0f;
// int entityId = 0; // int entityId = 0;
// Collection<String> otherParams = new ArrayList<>(0); // Collection<String> otherParams = new ArrayList<>(0);
// } // }
//} // }

View File

@ -1,18 +1,18 @@
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
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.ChangeGameTimeRspOuterClass.ChangeGameTimeRsp; import emu.grasscutter.net.proto.ChangeGameTimeRspOuterClass.ChangeGameTimeRsp;
public class PacketChangeGameTimeRsp extends BasePacket { public class PacketChangeGameTimeRsp extends BasePacket {
public PacketChangeGameTimeRsp(Player player) { public PacketChangeGameTimeRsp(Player player) {
super(PacketOpcodes.ChangeGameTimeRsp); super(PacketOpcodes.ChangeGameTimeRsp);
ChangeGameTimeRsp proto = ChangeGameTimeRsp proto =
ChangeGameTimeRsp.newBuilder().setCurGameTime(player.getWorld().getGameTime()).build(); ChangeGameTimeRsp.newBuilder().setCurGameTime(player.getWorld().getGameTime()).build();
this.setData(proto); this.setData(proto);
} }
} }

View File

@ -1,18 +1,18 @@
//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;
//import emu.grasscutter.net.proto.Unk2700FJEHHCPCBLGServerNotify; // import emu.grasscutter.net.proto.Unk2700FJEHHCPCBLGServerNotify;
// //
//public class PacketChangeHomeBgmNotify extends BasePacket { // public class PacketChangeHomeBgmNotify extends BasePacket {
// public PacketChangeHomeBgmNotify(int homeBgmId) { // public PacketChangeHomeBgmNotify(int homeBgmId) {
// super(PacketOpcodes.Unk2700_FJEHHCPCBLG_ServerNotify); // super(PacketOpcodes.Unk2700_FJEHHCPCBLG_ServerNotify);
// //
// var notify = // var notify =
// Unk2700FJEHHCPCBLGServerNotify.Unk2700_FJEHHCPCBLG_ServerNotify.newBuilder() // Unk2700FJEHHCPCBLGServerNotify.Unk2700_FJEHHCPCBLG_ServerNotify.newBuilder()
// .setUnk2700BJHAMKKECEI(homeBgmId) // .setUnk2700BJHAMKKECEI(homeBgmId)
// .build(); // .build();
// //
// this.setData(notify); // this.setData(notify);
// } // }
//} // }

View File

@ -1,18 +1,18 @@
//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;
//import emu.grasscutter.net.proto.Unk2700OGHMHELMBNNServerRsp; // import emu.grasscutter.net.proto.Unk2700OGHMHELMBNNServerRsp;
// //
//public class PacketChangeHomeBgmRsp extends BasePacket { // public class PacketChangeHomeBgmRsp extends BasePacket {
// public PacketChangeHomeBgmRsp() { // public PacketChangeHomeBgmRsp() {
// super(PacketOpcodes.Unk2700_OGHMHELMBNN_ServerRsp); // super(PacketOpcodes.Unk2700_OGHMHELMBNN_ServerRsp);
// //
// var rsp = // var rsp =
// Unk2700OGHMHELMBNNServerRsp.Unk2700_OGHMHELMBNN_ServerRsp.newBuilder() // Unk2700OGHMHELMBNNServerRsp.Unk2700_OGHMHELMBNN_ServerRsp.newBuilder()
// .setRetcode(0) // .setRetcode(0)
// .build(); // .build();
// //
// this.setData(rsp); // this.setData(rsp);
// } // }
//} // }

View File

@ -1,29 +1,29 @@
//package emu.grasscutter.server.packet.send; // package emu.grasscutter.server.packet.send;
// //
//import emu.grasscutter.game.player.Player; // import emu.grasscutter.game.player.Player;
//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.CustomTeamListNotifyOuterClass.CustomTeamListNotify; // import emu.grasscutter.net.proto.CustomTeamListNotifyOuterClass.CustomTeamListNotify;
// //
//public class PacketCustomTeamListNotify extends BasePacket { // public class PacketCustomTeamListNotify extends BasePacket {
// public PacketCustomTeamListNotify(Player player) { // public PacketCustomTeamListNotify(Player player) {
// super(PacketOpcodes.CustomTeamListNotify); // super(PacketOpcodes.CustomTeamListNotify);
// //
// CustomTeamListNotify.Builder proto = CustomTeamListNotify.newBuilder(); // CustomTeamListNotify.Builder proto = CustomTeamListNotify.newBuilder();
// //
// // Add the id list for custom teams. // // Add the id list for custom teams.
// for (int id : player.getTeamManager().getTeams().keySet()) { // for (int id : player.getTeamManager().getTeams().keySet()) {
// if (id > 4) { // if (id > 4) {
// proto.addCustomTeamIds(id); // proto.addCustomTeamIds(id);
// } // }
// } // }
// //
// // Add the avatar lists for all the teams the player has. // // Add the avatar lists for all the teams the player has.
// player // player
// .getTeamManager() // .getTeamManager()
// .getTeams() // .getTeams()
// .forEach((id, teamInfo) -> proto.putAvatarTeamMap(id, teamInfo.toProto(player))); // .forEach((id, teamInfo) -> proto.putAvatarTeamMap(id, teamInfo.toProto(player)));
// //
// this.setData(proto); // this.setData(proto);
// } // }
//} // }

View File

@ -1,13 +1,13 @@
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.dungeons.dungeon_results.BaseDungeonResult; 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;
public class PacketDungeonSettleNotify extends BasePacket { public class PacketDungeonSettleNotify extends BasePacket {
public PacketDungeonSettleNotify(BaseDungeonResult result) { public PacketDungeonSettleNotify(BaseDungeonResult result) {
super(PacketOpcodes.DungeonSettleNotify); super(PacketOpcodes.DungeonSettleNotify);
this.setData(result.getProto()); this.setData(result.getProto());
} }
} }

View File

@ -1,33 +1,31 @@
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
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.EntityFightPropUpdateNotifyOuterClass.EntityFightPropUpdateNotify; import emu.grasscutter.net.proto.EntityFightPropUpdateNotifyOuterClass.EntityFightPropUpdateNotify;
import java.util.Collection;
import java.util.Collection;
public class PacketEntityFightPropUpdateNotify extends BasePacket {
public class PacketEntityFightPropUpdateNotify extends BasePacket { public PacketEntityFightPropUpdateNotify(GameEntity entity, FightProperty prop) {
public PacketEntityFightPropUpdateNotify(GameEntity entity, FightProperty prop) { super(PacketOpcodes.EntityFightPropUpdateNotify);
super(PacketOpcodes.EntityFightPropUpdateNotify);
EntityFightPropUpdateNotify proto =
EntityFightPropUpdateNotify proto = EntityFightPropUpdateNotify.newBuilder()
EntityFightPropUpdateNotify.newBuilder() .setEntityId(entity.getId())
.setEntityId(entity.getId()) .putFightPropMap(prop.getId(), entity.getFightProperty(prop))
.putFightPropMap(prop.getId(), entity.getFightProperty(prop)) .build();
.build();
this.setData(proto);
this.setData(proto); }
}
public PacketEntityFightPropUpdateNotify(GameEntity entity, Collection<FightProperty> props) {
public PacketEntityFightPropUpdateNotify(GameEntity entity, Collection<FightProperty> props) { super(PacketOpcodes.EntityFightPropUpdateNotify);
super(PacketOpcodes.EntityFightPropUpdateNotify);
var protoBuilder = EntityFightPropUpdateNotify.newBuilder().setEntityId(entity.getId());
var protoBuilder = EntityFightPropUpdateNotify.newBuilder() props.forEach(p -> protoBuilder.putFightPropMap(p.getId(), entity.getFightProperty(p)));
.setEntityId(entity.getId());
props.forEach(p -> protoBuilder.putFightPropMap(p.getId(), entity.getFightProperty(p))); this.setData(protoBuilder);
}
this.setData(protoBuilder); }
}
}

View File

@ -1,26 +1,26 @@
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
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.quest.enums.ParentQuestState; import emu.grasscutter.game.quest.enums.ParentQuestState;
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.FinishedParentQuestNotifyOuterClass.FinishedParentQuestNotify; import emu.grasscutter.net.proto.FinishedParentQuestNotifyOuterClass.FinishedParentQuestNotify;
public class PacketFinishedParentQuestNotify extends BasePacket { public class PacketFinishedParentQuestNotify extends BasePacket {
public PacketFinishedParentQuestNotify(Player player) { public PacketFinishedParentQuestNotify(Player player) {
super(PacketOpcodes.FinishedParentQuestNotify, true); super(PacketOpcodes.FinishedParentQuestNotify, true);
FinishedParentQuestNotify.Builder proto = FinishedParentQuestNotify.newBuilder(); FinishedParentQuestNotify.Builder proto = FinishedParentQuestNotify.newBuilder();
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(false)); proto.addParentQuestList(mainQuest.toProto(false));
} }
} }
this.setData(proto); this.setData(proto);
} }
} }

View File

@ -1,33 +1,33 @@
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.quest.GameMainQuest; 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 {
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(true)) .addParentQuestList(quest.toProto(true))
.build(); .build();
this.setData(proto); this.setData(proto);
} }
public PacketFinishedParentQuestUpdateNotify(List<GameMainQuest> quests) { public PacketFinishedParentQuestUpdateNotify(List<GameMainQuest> quests) {
super(PacketOpcodes.FinishedParentQuestUpdateNotify); super(PacketOpcodes.FinishedParentQuestUpdateNotify);
var proto = FinishedParentQuestUpdateNotify.newBuilder(); var proto = FinishedParentQuestUpdateNotify.newBuilder();
for (GameMainQuest mainQuest : quests) { for (GameMainQuest mainQuest : quests) {
proto.addParentQuestList(mainQuest.toProto(true)); proto.addParentQuestList(mainQuest.toProto(true));
} }
proto.build(); proto.build();
this.setData(proto); this.setData(proto);
} }
} }

View File

@ -1,40 +1,42 @@
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
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.GetAllMailResultNotifyOuterClass.GetAllMailResultNotify; import emu.grasscutter.net.proto.GetAllMailResultNotifyOuterClass.GetAllMailResultNotify;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import java.time.Instant;
import java.time.Instant; import java.util.List;
import java.util.List;
public final class PacketGetAllMailResultNotify extends BasePacket {
public final class PacketGetAllMailResultNotify extends BasePacket { /**
/** * @param player The player to fetch the mail for.
* @param player The player to fetch the mail for. * @param gifts Is the mail for gifts?
* @param gifts Is the mail for gifts? */
*/ public PacketGetAllMailResultNotify(Player player, boolean gifts) {
public PacketGetAllMailResultNotify(Player player, boolean gifts) { super(PacketOpcodes.GetAllMailResultNotify);
super(PacketOpcodes.GetAllMailResultNotify);
var packet =
var packet = GetAllMailResultNotify.newBuilder() GetAllMailResultNotify.newBuilder()
.setTransaction(player.getUid() + "-" + Utils.getCurrentSeconds() + "-" + 0) .setTransaction(player.getUid() + "-" + Utils.getCurrentSeconds() + "-" + 0)
.setIsCollected(gifts) .setIsCollected(gifts)
.setPacketBeSentNum(1) .setPacketBeSentNum(1)
.setPacketNum(1); .setPacketNum(1);
var inbox = player.getAllMail(); var inbox = player.getAllMail();
if (!gifts && inbox.size() > 0) { if (!gifts && inbox.size() > 0) {
packet.addAllMailList(inbox.stream() packet.addAllMailList(
.filter(mail -> mail.stateValue == 1) inbox.stream()
.filter(mail -> mail.expireTime > Instant.now().getEpochSecond()) .filter(mail -> mail.stateValue == 1)
.map(mail -> mail.toProto(player)).toList()); .filter(mail -> mail.expireTime > Instant.now().getEpochSecond())
} else { .map(mail -> mail.toProto(player))
// Empty mailbox. .toList());
// TODO: Implement the gift mailbox. } else {
packet.addAllMailList(List.of()); // Empty mailbox.
} // TODO: Implement the gift mailbox.
packet.addAllMailList(List.of());
this.setData(packet.build()); }
}
} this.setData(packet.build());
}
}

View File

@ -1,18 +1,18 @@
//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;
//import emu.grasscutter.net.proto.HomeUnknown1NotifyOuterClass; // import emu.grasscutter.net.proto.HomeUnknown1NotifyOuterClass;
// //
//public class PacketHomeUnknown1Notify extends BasePacket { // public class PacketHomeUnknown1Notify extends BasePacket {
// //
// public PacketHomeUnknown1Notify(boolean isEnterEditMode) { // public PacketHomeUnknown1Notify(boolean isEnterEditMode) {
// super(PacketOpcodes.Unk2700_JDMPECKFGIG_ServerNotify); // super(PacketOpcodes.Unk2700_JDMPECKFGIG_ServerNotify);
// //
// var proto = HomeUnknown1NotifyOuterClass.HomeUnknown1Notify.newBuilder(); // var proto = HomeUnknown1NotifyOuterClass.HomeUnknown1Notify.newBuilder();
// //
// proto.setIsEnterEditMode(isEnterEditMode); // proto.setIsEnterEditMode(isEnterEditMode);
// //
// 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

@ -1,32 +1,33 @@
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;
import emu.grasscutter.net.proto.MusicGameSettleReqOuterClass; import emu.grasscutter.net.proto.MusicGameSettleReqOuterClass;
import emu.grasscutter.net.proto.MusicGameSettleRspOuterClass; import emu.grasscutter.net.proto.MusicGameSettleRspOuterClass;
import emu.grasscutter.net.proto.RetcodeOuterClass; import emu.grasscutter.net.proto.RetcodeOuterClass;
public class PacketMusicGameSettleRsp extends BasePacket { public class PacketMusicGameSettleRsp extends BasePacket {
public PacketMusicGameSettleRsp(int musicBasicId, long musicShareId, boolean isNewRecord) { public PacketMusicGameSettleRsp(int musicBasicId, long musicShareId, boolean isNewRecord) {
super(PacketOpcodes.MusicGameSettleRsp); super(PacketOpcodes.MusicGameSettleRsp);
var proto = MusicGameSettleRspOuterClass.MusicGameSettleRsp.newBuilder(); var proto = MusicGameSettleRspOuterClass.MusicGameSettleRsp.newBuilder();
proto.setMusicBasicId(musicBasicId).setUgcGuid(musicShareId).setIsNewRecord(isNewRecord); proto.setMusicBasicId(musicBasicId).setUgcGuid(musicShareId).setIsNewRecord(isNewRecord);
this.setData(proto); this.setData(proto);
} }
public PacketMusicGameSettleRsp(RetcodeOuterClass.Retcode errorCode, MusicGameSettleReqOuterClass.MusicGameSettleReq req) { public PacketMusicGameSettleRsp(
super(PacketOpcodes.MusicGameSettleRsp); RetcodeOuterClass.Retcode errorCode, MusicGameSettleReqOuterClass.MusicGameSettleReq req) {
super(PacketOpcodes.MusicGameSettleRsp);
var proto = MusicGameSettleRspOuterClass.MusicGameSettleRsp.newBuilder()
.setRetcode(errorCode.getNumber()) var proto =
.setMusicBasicId(req.getMusicBasicId()) MusicGameSettleRspOuterClass.MusicGameSettleRsp.newBuilder()
.setUgcGuid(req.getUgcGuid()); .setRetcode(errorCode.getNumber())
.setMusicBasicId(req.getMusicBasicId())
.setUgcGuid(req.getUgcGuid());
this.setData(proto);
} this.setData(proto);
} }
}

View File

@ -1,20 +1,21 @@
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
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.PlatformStartRouteNotify; import emu.grasscutter.net.proto.PlatformStartRouteNotifyOuterClass.PlatformStartRouteNotify;
import lombok.val; import lombok.val;
public class PacketPlatformStartRouteNotify extends BasePacket { public class PacketPlatformStartRouteNotify extends BasePacket {
public PacketPlatformStartRouteNotify(EntityGadget gadgetEntity) { public PacketPlatformStartRouteNotify(EntityGadget gadgetEntity) {
super(PacketOpcodes.PlatformStartRouteNotify); super(PacketOpcodes.PlatformStartRouteNotify);
val notify = PlatformStartRouteNotify.newBuilder() val notify =
.setEntityId(gadgetEntity.getId()) PlatformStartRouteNotify.newBuilder()
.setSceneTime(gadgetEntity.getScene().getSceneTime()) .setEntityId(gadgetEntity.getId())
.setPlatform(gadgetEntity.getPlatformInfo()); .setSceneTime(gadgetEntity.getScene().getSceneTime())
.setPlatform(gadgetEntity.getPlatformInfo());
this.setData(notify);
} this.setData(notify);
} }
}

View File

@ -1,20 +1,21 @@
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
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(EntityGadget gadgetEntity) { public PacketPlatformStopRouteNotify(EntityGadget gadgetEntity) {
super(PacketOpcodes.PlatformStopRouteNotify); super(PacketOpcodes.PlatformStopRouteNotify);
var notify = PlatformStopRouteNotifyOuterClass.PlatformStopRouteNotify.newBuilder() var notify =
.setPlatform(gadgetEntity.getPlatformInfo()) PlatformStopRouteNotifyOuterClass.PlatformStopRouteNotify.newBuilder()
.setSceneTime(gadgetEntity.getScene().getSceneTime()) .setPlatform(gadgetEntity.getPlatformInfo())
.setEntityId(gadgetEntity.getId()) .setSceneTime(gadgetEntity.getScene().getSceneTime())
.build(); .setEntityId(gadgetEntity.getId())
.build();
this.setData(notify);
} this.setData(notify);
} }
}

View File

@ -1,21 +1,21 @@
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
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.PlayerGameTimeNotifyOuterClass.PlayerGameTimeNotify; import emu.grasscutter.net.proto.PlayerGameTimeNotifyOuterClass.PlayerGameTimeNotify;
public class PacketPlayerGameTimeNotify extends BasePacket { public class PacketPlayerGameTimeNotify extends BasePacket {
public PacketPlayerGameTimeNotify(Player player) { public PacketPlayerGameTimeNotify(Player player) {
super(PacketOpcodes.PlayerGameTimeNotify); super(PacketOpcodes.PlayerGameTimeNotify);
PlayerGameTimeNotify proto = PlayerGameTimeNotify proto =
PlayerGameTimeNotify.newBuilder() PlayerGameTimeNotify.newBuilder()
.setGameTime(player.getWorld().getGameTime()) .setGameTime(player.getWorld().getGameTime())
.setUid(player.getUid()) .setUid(player.getUid())
.build(); .build();
this.setData(proto); this.setData(proto);
} }
} }

Some files were not shown because too many files have changed in this diff Show More