Revert final's added by 15e2f3ca34 (#2144)

* Revert final's added by 15e2f3ca34

* Apply suggestions from code review

Co-authored-by: Magix <27646710+KingRainbow44@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Magix <27646710+KingRainbow44@users.noreply.github.com>

---------

Co-authored-by: Magix <27646710+KingRainbow44@users.noreply.github.com>
This commit is contained in:
scooterboo 2023-05-06 23:39:16 -07:00 committed by GitHub
parent d18b4ffdd4
commit c832c1cac0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 1702 additions and 1702 deletions

View File

@ -1,72 +1,72 @@
package emu.grasscutter.auth;
import static emu.grasscutter.config.Configuration.ACCOUNT;
import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.auth.DefaultAuthenticators.*;
import emu.grasscutter.game.Account;
import emu.grasscutter.server.http.objects.ComboTokenResJson;
import emu.grasscutter.server.http.objects.LoginResultJson;
/**
* The default Grasscutter authentication implementation. Allows all users to access any account.
*/
public final class DefaultAuthentication implements AuthenticationSystem {
private final Authenticator<LoginResultJson> passwordAuthenticator;
private final Authenticator<LoginResultJson> tokenAuthenticator = new TokenAuthenticator();
private final Authenticator<ComboTokenResJson> sessionKeyAuthenticator =
new SessionKeyAuthenticator();
private final ExternalAuthenticator externalAuthenticator = new ExternalAuthentication();
private final OAuthAuthenticator oAuthAuthenticator = new OAuthAuthentication();
public DefaultAuthentication() {
if (ACCOUNT.EXPERIMENTAL_RealPassword) {
passwordAuthenticator = new ExperimentalPasswordAuthenticator();
} else {
passwordAuthenticator = new PasswordAuthenticator();
}
}
@Override
public void createAccount(String username, String password) {
// Unhandled. The default authenticator doesn't store passwords.
}
@Override
public void resetPassword(String username) {
// Unhandled. The default authenticator doesn't store passwords.
}
@Override
public Account verifyUser(String details) {
Grasscutter.getLogger()
.info(translate("messages.dispatch.authentication.default_unable_to_verify"));
return null;
}
@Override
public Authenticator<LoginResultJson> getPasswordAuthenticator() {
return this.passwordAuthenticator;
}
@Override
public Authenticator<LoginResultJson> getTokenAuthenticator() {
return this.tokenAuthenticator;
}
@Override
public Authenticator<ComboTokenResJson> getSessionKeyAuthenticator() {
return this.sessionKeyAuthenticator;
}
@Override
public ExternalAuthenticator getExternalAuthenticator() {
return this.externalAuthenticator;
}
@Override
public OAuthAuthenticator getOAuthAuthenticator() {
return this.oAuthAuthenticator;
}
}
package emu.grasscutter.auth;
import static emu.grasscutter.config.Configuration.ACCOUNT;
import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.auth.DefaultAuthenticators.*;
import emu.grasscutter.game.Account;
import emu.grasscutter.server.http.objects.ComboTokenResJson;
import emu.grasscutter.server.http.objects.LoginResultJson;
/**
* The default Grasscutter authentication implementation. Allows all users to access any account.
*/
public final class DefaultAuthentication implements AuthenticationSystem {
private final Authenticator<LoginResultJson> passwordAuthenticator;
private final Authenticator<LoginResultJson> tokenAuthenticator = new TokenAuthenticator();
private final Authenticator<ComboTokenResJson> sessionKeyAuthenticator =
new SessionKeyAuthenticator();
private final ExternalAuthenticator externalAuthenticator = new ExternalAuthentication();
private final OAuthAuthenticator oAuthAuthenticator = new OAuthAuthentication();
public DefaultAuthentication() {
if (ACCOUNT.EXPERIMENTAL_RealPassword) {
passwordAuthenticator = new ExperimentalPasswordAuthenticator();
} else {
passwordAuthenticator = new PasswordAuthenticator();
}
}
@Override
public void createAccount(String username, String password) {
// Unhandled. The default authenticator doesn't store passwords.
}
@Override
public void resetPassword(String username) {
// Unhandled. The default authenticator doesn't store passwords.
}
@Override
public Account verifyUser(String details) {
Grasscutter.getLogger()
.info(translate("messages.dispatch.authentication.default_unable_to_verify"));
return null;
}
@Override
public Authenticator<LoginResultJson> getPasswordAuthenticator() {
return this.passwordAuthenticator;
}
@Override
public Authenticator<LoginResultJson> getTokenAuthenticator() {
return this.tokenAuthenticator;
}
@Override
public Authenticator<ComboTokenResJson> getSessionKeyAuthenticator() {
return this.sessionKeyAuthenticator;
}
@Override
public ExternalAuthenticator getExternalAuthenticator() {
return this.externalAuthenticator;
}
@Override
public OAuthAuthenticator getOAuthAuthenticator() {
return this.oAuthAuthenticator;
}
}

View File

@ -1,190 +1,190 @@
package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Command(
label = "setStats",
aliases = {"stats", "stat"},
usage = {
"[set] <stat> <value>",
"(lock|freeze) <stat> [<value>]", // Can lock to current value
"(unlock|unfreeze) <stat>"
},
permission = "player.setstats",
permissionTargeted = "player.setstats.others")
public final class SetStatsCommand implements CommandHandler {
private final Map<String, Stat> stats;
public SetStatsCommand() {
this.stats = new HashMap<>();
for (String key : FightProperty.getShortNames()) {
this.stats.put(key, new Stat(FightProperty.getPropByShortName(key)));
}
// Full FightProperty enum that won't be advertised but can be used by devs
// They have a prefix to avoid the "hp" clash
for (FightProperty prop : FightProperty.values()) {
String name = prop.toString().substring(10); // FIGHT_PROP_BASE_HP -> _BASE_HP
String key = name.toLowerCase(); // _BASE_HP -> _base_hp
name = name.substring(1); // _BASE_HP -> BASE_HP
this.stats.put(key, new Stat(name, prop));
}
// Compatibility aliases
this.stats.put("mhp", this.stats.get("maxhp"));
this.stats.put("hp", this.stats.get("_cur_hp")); // Overrides FIGHT_PROP_HP
this.stats.put("atk", this.stats.get("_cur_attack")); // Overrides FIGHT_PROP_ATTACK
this.stats.put("def", this.stats.get("_cur_defense")); // Overrides FIGHT_PROP_DEFENSE
this.stats.put(
"atkb",
this.stats.get(
"_base_attack")); // This doesn't seem to get used to recalculate ATK, so it's only
// useful for stuff like Bennett's buff.
this.stats.put("eanemo", this.stats.get("anemo%"));
this.stats.put("ecryo", this.stats.get("cryo%"));
this.stats.put("edendro", this.stats.get("dendro%"));
this.stats.put("edend", this.stats.get("dendro%"));
this.stats.put("eelectro", this.stats.get("electro%"));
this.stats.put("eelec", this.stats.get("electro%"));
this.stats.put("ethunder", this.stats.get("electro%"));
this.stats.put("egeo", this.stats.get("geo%"));
this.stats.put("ehydro", this.stats.get("hydro%"));
this.stats.put("epyro", this.stats.get("pyro%"));
this.stats.put("ephys", this.stats.get("phys%"));
}
public static float parsePercent(String input) throws NumberFormatException {
if (input.endsWith("%")) {
return Float.parseFloat(input.substring(0, input.length() - 1)) / 100f;
} else {
return Float.parseFloat(input);
}
}
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
String statStr = null;
String valueStr;
float value = 0f;
if (args.size() < 2) {
sendUsageMessage(sender);
return;
}
// Get the action and stat
String arg0 = args.remove(0).toLowerCase();
Action action =
switch (arg0) {
default -> {
statStr = arg0;
yield Action.ACTION_SET;
} // Implicit set command
case "set" -> Action.ACTION_SET; // Explicit set command
case "lock", "freeze" -> Action.ACTION_LOCK;
case "unlock", "unfreeze" -> Action.ACTION_UNLOCK;
};
if (statStr == null) {
statStr = args.remove(0).toLowerCase();
}
if (!stats.containsKey(statStr)) {
sendUsageMessage(sender); // Invalid stat or action
return;
}
Stat stat = stats.get(statStr);
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
Avatar avatar = entity.getAvatar();
// Get the value if the action requires it
try {
switch (action) {
case ACTION_LOCK:
if (args.isEmpty()) { // Lock to current value
value = avatar.getFightProperty(stat.prop);
break;
} // Else fall-through and lock to supplied value
case ACTION_SET:
value = parsePercent(args.remove(0));
break;
case ACTION_UNLOCK:
break;
}
} catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.statValue");
return;
} catch (IndexOutOfBoundsException ignored) {
sendUsageMessage(sender);
return;
}
if (!args.isEmpty()) { // Leftover arguments!
sendUsageMessage(sender);
return;
}
switch (action) {
case ACTION_SET:
entity.setFightProperty(stat.prop, value);
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, stat.prop));
break;
case ACTION_LOCK:
avatar.getFightPropOverrides().put(stat.prop.getId(), value);
avatar.recalcStats();
break;
case ACTION_UNLOCK:
avatar.getFightPropOverrides().remove(stat.prop.getId());
avatar.recalcStats();
break;
}
// Report action
if (FightProperty.isPercentage(stat.prop)) {
valueStr = String.format("%.1f%%", value * 100f);
} else {
valueStr = String.format("%.0f", value);
}
if (targetPlayer == sender) {
CommandHandler.sendTranslatedMessage(sender, action.messageKeySelf, stat.name, valueStr);
} else {
String uidStr = targetPlayer.getAccount().getId();
CommandHandler.sendTranslatedMessage(
sender, action.messageKeyOther, stat.name, uidStr, valueStr);
}
}
private enum Action {
ACTION_SET("commands.generic.set_to", "commands.generic.set_for_to"),
ACTION_LOCK("commands.setStats.locked_to", "commands.setStats.locked_for_to"),
ACTION_UNLOCK("commands.setStats.unlocked", "commands.setStats.unlocked_for");
public final String messageKeySelf;
public final String messageKeyOther;
Action(String messageKeySelf, String messageKeyOther) {
this.messageKeySelf = messageKeySelf;
this.messageKeyOther = messageKeyOther;
}
}
private static class Stat {
String name;
FightProperty prop;
public Stat(FightProperty prop) {
this.name = prop.toString();
this.prop = prop;
}
public Stat(String name, FightProperty prop) {
this.name = name;
this.prop = prop;
}
}
}
package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Command(
label = "setStats",
aliases = {"stats", "stat"},
usage = {
"[set] <stat> <value>",
"(lock|freeze) <stat> [<value>]", // Can lock to current value
"(unlock|unfreeze) <stat>"
},
permission = "player.setstats",
permissionTargeted = "player.setstats.others")
public final class SetStatsCommand implements CommandHandler {
private final Map<String, Stat> stats;
public SetStatsCommand() {
this.stats = new HashMap<>();
for (String key : FightProperty.getShortNames()) {
this.stats.put(key, new Stat(FightProperty.getPropByShortName(key)));
}
// Full FightProperty enum that won't be advertised but can be used by devs
// They have a prefix to avoid the "hp" clash
for (FightProperty prop : FightProperty.values()) {
String name = prop.toString().substring(10); // FIGHT_PROP_BASE_HP -> _BASE_HP
String key = name.toLowerCase(); // _BASE_HP -> _base_hp
name = name.substring(1); // _BASE_HP -> BASE_HP
this.stats.put(key, new Stat(name, prop));
}
// Compatibility aliases
this.stats.put("mhp", this.stats.get("maxhp"));
this.stats.put("hp", this.stats.get("_cur_hp")); // Overrides FIGHT_PROP_HP
this.stats.put("atk", this.stats.get("_cur_attack")); // Overrides FIGHT_PROP_ATTACK
this.stats.put("def", this.stats.get("_cur_defense")); // Overrides FIGHT_PROP_DEFENSE
this.stats.put(
"atkb",
this.stats.get(
"_base_attack")); // This doesn't seem to get used to recalculate ATK, so it's only
// useful for stuff like Bennett's buff.
this.stats.put("eanemo", this.stats.get("anemo%"));
this.stats.put("ecryo", this.stats.get("cryo%"));
this.stats.put("edendro", this.stats.get("dendro%"));
this.stats.put("edend", this.stats.get("dendro%"));
this.stats.put("eelectro", this.stats.get("electro%"));
this.stats.put("eelec", this.stats.get("electro%"));
this.stats.put("ethunder", this.stats.get("electro%"));
this.stats.put("egeo", this.stats.get("geo%"));
this.stats.put("ehydro", this.stats.get("hydro%"));
this.stats.put("epyro", this.stats.get("pyro%"));
this.stats.put("ephys", this.stats.get("phys%"));
}
public static float parsePercent(String input) throws NumberFormatException {
if (input.endsWith("%")) {
return Float.parseFloat(input.substring(0, input.length() - 1)) / 100f;
} else {
return Float.parseFloat(input);
}
}
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
String statStr = null;
String valueStr;
float value = 0f;
if (args.size() < 2) {
sendUsageMessage(sender);
return;
}
// Get the action and stat
String arg0 = args.remove(0).toLowerCase();
Action action =
switch (arg0) {
default -> {
statStr = arg0;
yield Action.ACTION_SET;
} // Implicit set command
case "set" -> Action.ACTION_SET; // Explicit set command
case "lock", "freeze" -> Action.ACTION_LOCK;
case "unlock", "unfreeze" -> Action.ACTION_UNLOCK;
};
if (statStr == null) {
statStr = args.remove(0).toLowerCase();
}
if (!stats.containsKey(statStr)) {
sendUsageMessage(sender); // Invalid stat or action
return;
}
Stat stat = stats.get(statStr);
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
Avatar avatar = entity.getAvatar();
// Get the value if the action requires it
try {
switch (action) {
case ACTION_LOCK:
if (args.isEmpty()) { // Lock to current value
value = avatar.getFightProperty(stat.prop);
break;
} // Else fall-through and lock to supplied value
case ACTION_SET:
value = parsePercent(args.remove(0));
break;
case ACTION_UNLOCK:
break;
}
} catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.statValue");
return;
} catch (IndexOutOfBoundsException ignored) {
sendUsageMessage(sender);
return;
}
if (!args.isEmpty()) { // Leftover arguments!
sendUsageMessage(sender);
return;
}
switch (action) {
case ACTION_SET:
entity.setFightProperty(stat.prop, value);
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, stat.prop));
break;
case ACTION_LOCK:
avatar.getFightPropOverrides().put(stat.prop.getId(), value);
avatar.recalcStats();
break;
case ACTION_UNLOCK:
avatar.getFightPropOverrides().remove(stat.prop.getId());
avatar.recalcStats();
break;
}
// Report action
if (FightProperty.isPercentage(stat.prop)) {
valueStr = String.format("%.1f%%", value * 100f);
} else {
valueStr = String.format("%.0f", value);
}
if (targetPlayer == sender) {
CommandHandler.sendTranslatedMessage(sender, action.messageKeySelf, stat.name, valueStr);
} else {
String uidStr = targetPlayer.getAccount().getId();
CommandHandler.sendTranslatedMessage(
sender, action.messageKeyOther, stat.name, uidStr, valueStr);
}
}
private enum Action {
ACTION_SET("commands.generic.set_to", "commands.generic.set_for_to"),
ACTION_LOCK("commands.setStats.locked_to", "commands.setStats.locked_for_to"),
ACTION_UNLOCK("commands.setStats.unlocked", "commands.setStats.unlocked_for");
public final String messageKeySelf;
public final String messageKeyOther;
Action(String messageKeySelf, String messageKeyOther) {
this.messageKeySelf = messageKeySelf;
this.messageKeyOther = messageKeyOther;
}
}
private static class Stat {
String name;
FightProperty prop;
public Stat(FightProperty prop) {
this.name = prop.toString();
this.prop = prop;
}
public Stat(String name, FightProperty prop) {
this.name = name;
this.prop = prop;
}
}
}

View File

@ -477,8 +477,8 @@ public final class GameData {
private static final Int2IntMap trialAvatarIndexIdTrialActivityDataDataMap =
new Int2IntOpenHashMap();
private static final Map<Integer, List<Integer>> fetters = new HashMap<>();
private static final Map<Integer, List<ShopGoodsData>> shopGoods = new HashMap<>();
private static Map<Integer, List<Integer>> fetters = new HashMap<>();
private static Map<Integer, List<ShopGoodsData>> shopGoods = new HashMap<>();
// Getters with different names that stay for now
public static Int2ObjectMap<MainQuestData> getMainQuestDataMap() {

View File

@ -19,17 +19,17 @@ import lombok.Setter;
public class GameDepot {
public static final int[] BLOCK_SIZE = new int[] {50, 500}; // Scales
private static final Int2ObjectMap<WeightedList<ReliquaryMainPropData>> relicRandomMainPropDepot =
private static Int2ObjectMap<WeightedList<ReliquaryMainPropData>> relicRandomMainPropDepot =
new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<List<ReliquaryMainPropData>> relicMainPropDepot =
private static Int2ObjectMap<List<ReliquaryMainPropData>> relicMainPropDepot =
new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<List<ReliquaryAffixData>> relicAffixDepot =
private static Int2ObjectMap<List<ReliquaryAffixData>> relicAffixDepot =
new Int2ObjectOpenHashMap<>();
@Getter @Setter private static Map<String, AvatarConfig> playerAbilities = new HashMap<>();
@Getter
private static final HashMap<SpawnDataEntry.GridBlockId, ArrayList<SpawnDataEntry>> spawnLists =
private static HashMap<SpawnDataEntry.GridBlockId, ArrayList<SpawnDataEntry>> spawnLists =
new HashMap<>();
@Getter @Setter private static BlossomConfig blossomConfig;

View File

@ -1,35 +1,35 @@
package emu.grasscutter.data.binout;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
import java.util.ArrayList;
import java.util.List;
public class AbilityModifierEntry {
public List<AbilityModifierAction> onModifierAdded;
public List<AbilityModifierAction> onThinkInterval;
public List<AbilityModifierAction> onRemoved;
private final String name; // Custom value
public AbilityModifierEntry(String name) {
this.name = name;
this.onModifierAdded = new ArrayList<>();
this.onThinkInterval = new ArrayList<>();
this.onRemoved = new ArrayList<>();
}
public String getName() {
return name;
}
public List<AbilityModifierAction> getOnAdded() {
return onModifierAdded;
}
public List<AbilityModifierAction> getOnThinkInterval() {
return onThinkInterval;
}
public List<AbilityModifierAction> getOnRemoved() {
return onRemoved;
}
}
package emu.grasscutter.data.binout;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
import java.util.ArrayList;
import java.util.List;
public class AbilityModifierEntry {
public List<AbilityModifierAction> onModifierAdded;
public List<AbilityModifierAction> onThinkInterval;
public List<AbilityModifierAction> onRemoved;
private final String name; // Custom value
public AbilityModifierEntry(String name) {
this.name = name;
this.onModifierAdded = new ArrayList<>();
this.onThinkInterval = new ArrayList<>();
this.onRemoved = new ArrayList<>();
}
public String getName() {
return name;
}
public List<AbilityModifierAction> getOnAdded() {
return onModifierAdded;
}
public List<AbilityModifierAction> getOnThinkInterval() {
return onThinkInterval;
}
public List<AbilityModifierAction> getOnRemoved() {
return onRemoved;
}
}

View File

@ -1,71 +1,71 @@
package emu.grasscutter.data.binout;
import emu.grasscutter.data.ResourceLoader.OpenConfigData;
import java.util.ArrayList;
import java.util.List;
public class OpenConfigEntry {
private final String name;
private String[] addAbilities;
private int extraTalentIndex;
private SkillPointModifier[] skillPointModifiers;
public OpenConfigEntry(String name, OpenConfigData[] data) {
this.name = name;
List<String> abilityList = new ArrayList<>();
List<SkillPointModifier> modList = new ArrayList<>();
for (OpenConfigData entry : data) {
if (entry.$type.contains("AddAbility")) {
abilityList.add(entry.abilityName);
} else if (entry.talentIndex > 0) {
this.extraTalentIndex = entry.talentIndex;
} else if (entry.$type.contains("ModifySkillPoint")) {
modList.add(new SkillPointModifier(entry.skillID, entry.pointDelta));
}
}
if (abilityList.size() > 0) {
this.addAbilities = abilityList.toArray(new String[0]);
}
if (modList.size() > 0) {
this.skillPointModifiers = modList.toArray(new SkillPointModifier[0]);
}
}
public String getName() {
return name;
}
public String[] getAddAbilities() {
return addAbilities;
}
public int getExtraTalentIndex() {
return extraTalentIndex;
}
public SkillPointModifier[] getSkillPointModifiers() {
return skillPointModifiers;
}
public static class SkillPointModifier {
private final int skillId;
private final int delta;
public SkillPointModifier(int skillId, int delta) {
this.skillId = skillId;
this.delta = delta;
}
public int getSkillId() {
return skillId;
}
public int getDelta() {
return delta;
}
}
}
package emu.grasscutter.data.binout;
import emu.grasscutter.data.ResourceLoader.OpenConfigData;
import java.util.ArrayList;
import java.util.List;
public class OpenConfigEntry {
private final String name;
private String[] addAbilities;
private int extraTalentIndex;
private SkillPointModifier[] skillPointModifiers;
public OpenConfigEntry(String name, OpenConfigData[] data) {
this.name = name;
List<String> abilityList = new ArrayList<>();
List<SkillPointModifier> modList = new ArrayList<>();
for (OpenConfigData entry : data) {
if (entry.$type.contains("AddAbility")) {
abilityList.add(entry.abilityName);
} else if (entry.talentIndex > 0) {
this.extraTalentIndex = entry.talentIndex;
} else if (entry.$type.contains("ModifySkillPoint")) {
modList.add(new SkillPointModifier(entry.skillID, entry.pointDelta));
}
}
if (abilityList.size() > 0) {
this.addAbilities = abilityList.toArray(new String[0]);
}
if (modList.size() > 0) {
this.skillPointModifiers = modList.toArray(new SkillPointModifier[0]);
}
}
public String getName() {
return name;
}
public String[] getAddAbilities() {
return addAbilities;
}
public int getExtraTalentIndex() {
return extraTalentIndex;
}
public SkillPointModifier[] getSkillPointModifiers() {
return skillPointModifiers;
}
public static class SkillPointModifier {
private int skillId;
private int delta;
public SkillPointModifier(int skillId, int delta) {
this.skillId = skillId;
this.delta = delta;
}
public int getSkillId() {
return skillId;
}
public int getDelta() {
return delta;
}
}
}

View File

@ -31,7 +31,7 @@ public class ItemData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
private final int stackLimit = 1;
private int stackLimit = 1;
private int maxUseCount;
private int rankLevel;
private String effectName;
@ -43,7 +43,7 @@ public class ItemData extends GameResource {
private int[] destroyReturnMaterialCount;
// Enums
private final ItemType itemType = ItemType.ITEM_NONE;
private ItemType itemType = ItemType.ITEM_NONE;
private MaterialType materialType = MaterialType.MATERIAL_NONE;
private EquipType equipType = EquipType.EQUIP_NONE;
private String effectType;

View File

@ -1,21 +1,21 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Getter;
@ResourceType(name = "PlayerLevelExcelConfigData.json")
@Getter
public class PlayerLevelData extends GameResource {
private int level;
private int exp;
private int rewardId;
private final int expeditionLimitAdd = 0;
private int unlockWorldLevel;
private long unlockDescTextMapHash;
@Override
public int getId() {
return this.level;
}
}
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Getter;
@ResourceType(name = "PlayerLevelExcelConfigData.json")
@Getter
public class PlayerLevelData extends GameResource {
private int level;
private int exp;
private int rewardId;
private int expeditionLimitAdd = 0;
private int unlockWorldLevel;
private long unlockDescTextMapHash;
@Override
public int getId() {
return this.level;
}
}

View File

@ -18,7 +18,7 @@ public class AchievementData extends GameResource {
private static final AtomicBoolean isDivided = new AtomicBoolean();
private int goalId;
private int preStageAchievementId;
private final Set<Integer> groupAchievementIdList = new HashSet<>();
private Set<Integer> groupAchievementIdList = new HashSet<>();
private boolean isParent;
private long titleTextMapHash;
private long descTextMapHash;

View File

@ -26,7 +26,7 @@ public class Account {
private String token;
private String sessionKey; // Session token for dispatch server
private final List<String> permissions;
private List<String> permissions;
private Locale locale;
private String banReason;

View File

@ -1,214 +1,214 @@
package emu.grasscutter.game.ability;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
import emu.grasscutter.net.proto.AbilityMetaModifierChangeOuterClass.AbilityMetaModifierChange;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class HealAbilityManager {
ArrayList<HealDataAvatar> healDataAvatarList;
private final Player player;
public HealAbilityManager(Player player) {
this.player = player;
healDataAvatarList = new ArrayList();
healDataAvatarList.add(
new HealDataAvatar(10000054, "Kokomi", 0)
.addHealData(
"E",
"ElementalArt_Heal_MaxHP_Base_Percentage",
"ElementalArt_Heal_Base_Amount",
false)
.addHealData("Q", "Avatar_Kokomi_ElementalBurst_Heal", 0.0172f, 212f, false));
healDataAvatarList.add(
new HealDataAvatar(10000003, "Qin", 1).addHealData("Q", "Heal", "BurstHealConst", true));
healDataAvatarList.add(
new HealDataAvatar(10000034, "Noel", 2)
.addHealData("E", "OnAttack_HealthRate", 0.452f, 282f, true));
healDataAvatarList.add(
new HealDataAvatar(10000032, "Bennett", 0)
.addHealData("Q", "HealMaxHpRatio", "HealConst", false));
healDataAvatarList.add(
new HealDataAvatar(10000039, "Diona", 0)
.addHealData("Q", "HealHPRatio", "HealHP_Const", false));
healDataAvatarList.add(
new HealDataAvatar(10000053, "Sayu", 1)
.addHealData("Q", "Constellation_6_Damage", "Heal_BaseAmount", true)
.addHealData("Q", "Heal_AttackRatio", "Constellation_6_Heal", true));
healDataAvatarList.add(
new HealDataAvatar(10000014, "Barbara", 0)
.addHealData("E", "HealHPOnAdded", "HealHPOnAdded_Const", true)
.addHealData("E", "HealHP_OnHittingOthers", "HealHP_Const_OnHittingOthers", true)
.addHealData("Q", "Avatar_Barbara_IdolHeal", 0.374f, 4660f, true));
healDataAvatarList.add(
new HealDataAvatar(10000065, "Shinobu", 0)
.addHealData("E", "ElementalArt_Heal_MaxHP_Percentage", 0.064f, 795f, false));
healDataAvatarList.add(
new HealDataAvatar(10000035, "Qiqi", 1)
.addHealData("E", "HealHP_OnHittingOthers", "HealHP_Const_OnHittingOthers", true)
.addHealData("E", "ElementalArt_HealHp_Ratio", "ElementalArt_HealHp_Const", true)
.addHealData("Q", "Avatar_Qiqi_ElementalBurst_ApplyModifier", 0.0191f, 1588f, false));
healDataAvatarList.add(
new HealDataAvatar(10000046, "Hutao", 0)
.addHealData("Q", "Avatar_Hutao_VermilionBite_BakeMesh", 0.1166f, 0f, false));
}
public Player getPlayer() {
return this.player;
}
public void healHandler(AbilityInvokeEntry invoke) throws Exception {
AbilityMetaModifierChange data = AbilityMetaModifierChange.parseFrom(invoke.getAbilityData());
if (data == null) {
return;
}
GameEntity sourceEntity = player.getScene().getEntityById(data.getApplyEntityId());
String modifierString = "";
if (data.getParentAbilityName() != null) modifierString = data.getParentAbilityName().getStr();
if (sourceEntity != null) checkAndHeal(sourceEntity, modifierString);
}
public void checkAndHeal(GameEntity sourceEntity, String modifierString) {
int fightPropertyType = 0;
float healAmount = 0;
float ratio = 0, base = 0;
float maxHP, curHP, curAttack, curDefense;
Map<String, Float> map = sourceEntity.getMetaOverrideMap();
for (int i = 0; i < healDataAvatarList.size(); i++) {
HealDataAvatar healDataAvatar = healDataAvatarList.get(i);
if (modifierString.contains(healDataAvatar.avatarName)) {
fightPropertyType = healDataAvatar.fightPropertyType;
ArrayList<HealData> healDataList = healDataAvatar.healDataList;
for (int j = 0; j < healDataList.size(); j++) {
HealData healData = healDataList.get(j);
if (map.containsKey(healData.sRatio) || modifierString.equals(healData.sRatio)) {
if (healData.isString) {
ratio = map.get(healData.sRatio);
base = map.get(healData.sBase);
} else {
ratio = healData.fRatio;
base = healData.fBase;
}
}
List<EntityAvatar> activeTeam = player.getTeamManager().getActiveTeam();
List<EntityAvatar> needHealAvatars = new ArrayList();
int currentIndex = player.getTeamManager().getCurrentCharacterIndex();
EntityAvatar currentAvatar = activeTeam.get(currentIndex);
if (healData.healAll) {
needHealAvatars = activeTeam;
} else {
needHealAvatars.add(currentAvatar);
}
EntityAvatar healActionAvatar = null;
for (int k = 0; k < activeTeam.size(); k++) {
EntityAvatar avatar = activeTeam.get(k);
int avatarId = avatar.getAvatar().getAvatarId();
if (avatarId == healDataAvatar.avatarId) {
healActionAvatar = avatar;
break;
}
}
if (healActionAvatar != null) {
maxHP = healActionAvatar.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
curHP = healActionAvatar.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
curAttack = healActionAvatar.getFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK);
curDefense = healActionAvatar.getFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE);
// Special case for Hu Tao:
if (healDataAvatar.avatarName.equals("Hutao") && curHP <= maxHP * 0.5 && ratio != 0) {
ratio = 0.1555f;
}
switch (fightPropertyType) {
case 0:
healAmount = ratio * maxHP + base;
break;
case 1:
healAmount = ratio * curAttack + base;
break;
case 2:
healAmount = ratio * curDefense + base;
break;
}
}
for (int k = 0; k < needHealAvatars.size(); k++) {
EntityAvatar avatar = needHealAvatars.get(k);
avatar.heal(healAmount);
}
}
break;
}
}
}
private class HealData {
public boolean isString = true;
public String abilityType = ""; // "E" or "Q"
public String sRatio = "";
public String sBase = "";
public float fRatio = 0;
public float fBase = 0;
public boolean healAll = false;
public HealData(String _abilityType, String _sRatio, String _sBase, boolean _healAll) {
abilityType = _abilityType;
isString = true;
sRatio = _sRatio;
sBase = _sBase;
healAll = _healAll;
}
public HealData(
String _abilityType, String _sRatio, float _fRatio, float _fBase, boolean _healAll) {
abilityType = _abilityType;
isString = false;
sRatio = _sRatio;
fRatio = _fRatio;
fBase = _fBase;
healAll = _healAll;
}
}
private class HealDataAvatar {
public int avatarId = 0;
public String avatarName = "";
public int fightPropertyType = 0; // 0: maxHP, 1: curAttack, 2: curDefense
public ArrayList<HealData> healDataList;
public HealDataAvatar(int _avatarId, String _avatarName, int _fightPropertyType) {
avatarId = _avatarId;
avatarName = _avatarName;
fightPropertyType = _fightPropertyType;
healDataList = new ArrayList();
}
public HealDataAvatar addHealData(
String abilityType, String sRatio, String sBase, boolean healAll) {
HealData healData = new HealData(abilityType, sRatio, sBase, healAll);
healDataList.add(healData);
return this;
}
public HealDataAvatar addHealData(
String abilityType, String sRatio, float fRatio, float fBase, boolean healAll) {
HealData healData = new HealData(abilityType, sRatio, fRatio, fBase, healAll);
healDataList.add(healData);
return this;
}
}
}
package emu.grasscutter.game.ability;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
import emu.grasscutter.net.proto.AbilityMetaModifierChangeOuterClass.AbilityMetaModifierChange;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class HealAbilityManager {
ArrayList<HealDataAvatar> healDataAvatarList;
private Player player;
public HealAbilityManager(Player player) {
this.player = player;
healDataAvatarList = new ArrayList();
healDataAvatarList.add(
new HealDataAvatar(10000054, "Kokomi", 0)
.addHealData(
"E",
"ElementalArt_Heal_MaxHP_Base_Percentage",
"ElementalArt_Heal_Base_Amount",
false)
.addHealData("Q", "Avatar_Kokomi_ElementalBurst_Heal", 0.0172f, 212f, false));
healDataAvatarList.add(
new HealDataAvatar(10000003, "Qin", 1).addHealData("Q", "Heal", "BurstHealConst", true));
healDataAvatarList.add(
new HealDataAvatar(10000034, "Noel", 2)
.addHealData("E", "OnAttack_HealthRate", 0.452f, 282f, true));
healDataAvatarList.add(
new HealDataAvatar(10000032, "Bennett", 0)
.addHealData("Q", "HealMaxHpRatio", "HealConst", false));
healDataAvatarList.add(
new HealDataAvatar(10000039, "Diona", 0)
.addHealData("Q", "HealHPRatio", "HealHP_Const", false));
healDataAvatarList.add(
new HealDataAvatar(10000053, "Sayu", 1)
.addHealData("Q", "Constellation_6_Damage", "Heal_BaseAmount", true)
.addHealData("Q", "Heal_AttackRatio", "Constellation_6_Heal", true));
healDataAvatarList.add(
new HealDataAvatar(10000014, "Barbara", 0)
.addHealData("E", "HealHPOnAdded", "HealHPOnAdded_Const", true)
.addHealData("E", "HealHP_OnHittingOthers", "HealHP_Const_OnHittingOthers", true)
.addHealData("Q", "Avatar_Barbara_IdolHeal", 0.374f, 4660f, true));
healDataAvatarList.add(
new HealDataAvatar(10000065, "Shinobu", 0)
.addHealData("E", "ElementalArt_Heal_MaxHP_Percentage", 0.064f, 795f, false));
healDataAvatarList.add(
new HealDataAvatar(10000035, "Qiqi", 1)
.addHealData("E", "HealHP_OnHittingOthers", "HealHP_Const_OnHittingOthers", true)
.addHealData("E", "ElementalArt_HealHp_Ratio", "ElementalArt_HealHp_Const", true)
.addHealData("Q", "Avatar_Qiqi_ElementalBurst_ApplyModifier", 0.0191f, 1588f, false));
healDataAvatarList.add(
new HealDataAvatar(10000046, "Hutao", 0)
.addHealData("Q", "Avatar_Hutao_VermilionBite_BakeMesh", 0.1166f, 0f, false));
}
public Player getPlayer() {
return this.player;
}
public void healHandler(AbilityInvokeEntry invoke) throws Exception {
AbilityMetaModifierChange data = AbilityMetaModifierChange.parseFrom(invoke.getAbilityData());
if (data == null) {
return;
}
GameEntity sourceEntity = player.getScene().getEntityById(data.getApplyEntityId());
String modifierString = "";
if (data.getParentAbilityName() != null) modifierString = data.getParentAbilityName().getStr();
if (sourceEntity != null) checkAndHeal(sourceEntity, modifierString);
}
public void checkAndHeal(GameEntity sourceEntity, String modifierString) {
int fightPropertyType = 0;
float healAmount = 0;
float ratio = 0, base = 0;
float maxHP, curHP, curAttack, curDefense;
Map<String, Float> map = sourceEntity.getMetaOverrideMap();
for (int i = 0; i < healDataAvatarList.size(); i++) {
HealDataAvatar healDataAvatar = healDataAvatarList.get(i);
if (modifierString.contains(healDataAvatar.avatarName)) {
fightPropertyType = healDataAvatar.fightPropertyType;
ArrayList<HealData> healDataList = healDataAvatar.healDataList;
for (int j = 0; j < healDataList.size(); j++) {
HealData healData = healDataList.get(j);
if (map.containsKey(healData.sRatio) || modifierString.equals(healData.sRatio)) {
if (healData.isString) {
ratio = map.get(healData.sRatio);
base = map.get(healData.sBase);
} else {
ratio = healData.fRatio;
base = healData.fBase;
}
}
List<EntityAvatar> activeTeam = player.getTeamManager().getActiveTeam();
List<EntityAvatar> needHealAvatars = new ArrayList();
int currentIndex = player.getTeamManager().getCurrentCharacterIndex();
EntityAvatar currentAvatar = activeTeam.get(currentIndex);
if (healData.healAll) {
needHealAvatars = activeTeam;
} else {
needHealAvatars.add(currentAvatar);
}
EntityAvatar healActionAvatar = null;
for (int k = 0; k < activeTeam.size(); k++) {
EntityAvatar avatar = activeTeam.get(k);
int avatarId = avatar.getAvatar().getAvatarId();
if (avatarId == healDataAvatar.avatarId) {
healActionAvatar = avatar;
break;
}
}
if (healActionAvatar != null) {
maxHP = healActionAvatar.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
curHP = healActionAvatar.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
curAttack = healActionAvatar.getFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK);
curDefense = healActionAvatar.getFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE);
// Special case for Hu Tao:
if (healDataAvatar.avatarName.equals("Hutao") && curHP <= maxHP * 0.5 && ratio != 0) {
ratio = 0.1555f;
}
switch (fightPropertyType) {
case 0:
healAmount = ratio * maxHP + base;
break;
case 1:
healAmount = ratio * curAttack + base;
break;
case 2:
healAmount = ratio * curDefense + base;
break;
}
}
for (int k = 0; k < needHealAvatars.size(); k++) {
EntityAvatar avatar = needHealAvatars.get(k);
avatar.heal(healAmount);
}
}
break;
}
}
}
private class HealData {
public boolean isString = true;
public String abilityType = ""; // "E" or "Q"
public String sRatio = "";
public String sBase = "";
public float fRatio = 0;
public float fBase = 0;
public boolean healAll = false;
public HealData(String _abilityType, String _sRatio, String _sBase, boolean _healAll) {
abilityType = _abilityType;
isString = true;
sRatio = _sRatio;
sBase = _sBase;
healAll = _healAll;
}
public HealData(
String _abilityType, String _sRatio, float _fRatio, float _fBase, boolean _healAll) {
abilityType = _abilityType;
isString = false;
sRatio = _sRatio;
fRatio = _fRatio;
fBase = _fBase;
healAll = _healAll;
}
}
private class HealDataAvatar {
public int avatarId = 0;
public String avatarName = "";
public int fightPropertyType = 0; // 0: maxHP, 1: curAttack, 2: curDefense
public ArrayList<HealData> healDataList;
public HealDataAvatar(int _avatarId, String _avatarName, int _fightPropertyType) {
avatarId = _avatarId;
avatarName = _avatarName;
fightPropertyType = _fightPropertyType;
healDataList = new ArrayList();
}
public HealDataAvatar addHealData(
String abilityType, String sRatio, String sBase, boolean healAll) {
HealData healData = new HealData(abilityType, sRatio, sBase, healAll);
healDataList.add(healData);
return this;
}
public HealDataAvatar addHealData(
String abilityType, String sRatio, float fRatio, float fBase, boolean healAll) {
HealData healData = new HealData(abilityType, sRatio, fRatio, fBase, healAll);
healDataList.add(healData);
return this;
}
}
}

View File

@ -1,40 +1,40 @@
package emu.grasscutter.game.achievement;
import dev.morphia.annotations.Entity;
import emu.grasscutter.net.proto.AchievementOuterClass;
import emu.grasscutter.net.proto.StatusOuterClass;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter
public class Achievement {
@Setter private StatusOuterClass.Status status;
private final int id;
private final int totalProgress;
@Setter private int curProgress;
@Setter private int finishTimestampSec;
public Achievement(
StatusOuterClass.Status status,
int id,
int totalProgress,
int curProgress,
int finishTimestampSec) {
this.status = status;
this.id = id;
this.totalProgress = totalProgress;
this.curProgress = curProgress;
this.finishTimestampSec = finishTimestampSec;
}
public AchievementOuterClass.Achievement toProto() {
return AchievementOuterClass.Achievement.newBuilder()
.setStatus(this.getStatus())
.setId(this.getId())
.setTotalProgress(this.getTotalProgress())
.setCurProgress(this.getCurProgress())
.setFinishTimestamp(this.getFinishTimestampSec())
.build();
}
}
package emu.grasscutter.game.achievement;
import dev.morphia.annotations.Entity;
import emu.grasscutter.net.proto.AchievementOuterClass;
import emu.grasscutter.net.proto.StatusOuterClass;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter
public class Achievement {
@Setter private StatusOuterClass.Status status;
private int id;
private int totalProgress;
@Setter private int curProgress;
@Setter private int finishTimestampSec;
public Achievement(
StatusOuterClass.Status status,
int id,
int totalProgress,
int curProgress,
int finishTimestampSec) {
this.status = status;
this.id = id;
this.totalProgress = totalProgress;
this.curProgress = curProgress;
this.finishTimestampSec = finishTimestampSec;
}
public AchievementOuterClass.Achievement toProto() {
return AchievementOuterClass.Achievement.newBuilder()
.setStatus(this.getStatus())
.setId(this.getId())
.setTotalProgress(this.getTotalProgress())
.setCurProgress(this.getCurProgress())
.setFinishTimestamp(this.getFinishTimestampSec())
.build();
}
}

View File

@ -74,13 +74,13 @@ public class Avatar {
private List<Integer> fetters;
private final Map<Integer, Integer> skillLevelMap = new Int2IntArrayMap(7); // Talent levels
private Map<Integer, Integer> skillLevelMap = new Int2IntArrayMap(7); // Talent levels
@Transient @Getter
private final Map<Integer, Integer> skillExtraChargeMap = new Int2IntArrayMap(2); // Charges
private Map<Integer, Integer> skillExtraChargeMap = new Int2IntArrayMap(2); // Charges
@Transient
private final Map<Integer, Integer> proudSkillBonusMap =
private Map<Integer, Integer> proudSkillBonusMap =
new Int2IntArrayMap(2); // Talent bonus levels (from const)
@Getter private int skillDepotId;

View File

@ -1,122 +1,122 @@
package emu.grasscutter.game.entity;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
import emu.grasscutter.net.proto.ClientGadgetInfoOuterClass;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData;
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
import emu.grasscutter.net.proto.EvtCreateGadgetNotifyOuterClass.EvtCreateGadgetNotify;
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType;
import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter;
public class EntityClientGadget extends EntityBaseGadget {
@Getter private final Player owner;
@Getter(onMethod_ = @Override)
private final int gadgetId;
@Getter private final int campId;
@Getter private final int campType;
@Getter private final int ownerEntityId;
@Getter private final int targetEntityId;
@Getter private final boolean asyncLoad;
@Getter private final int originalOwnerEntityId;
public EntityClientGadget(Scene scene, Player player, EvtCreateGadgetNotify notify) {
super(scene, new Position(notify.getInitPos()), new Position(notify.getInitEulerAngles()));
this.owner = player;
this.id = notify.getEntityId();
this.gadgetId = notify.getConfigId();
this.campId = notify.getCampId();
this.campType = notify.getCampType();
this.ownerEntityId = notify.getPropOwnerEntityId();
this.targetEntityId = notify.getTargetEntityId();
this.asyncLoad = notify.getIsAsyncLoad();
GameEntity owner = scene.getEntityById(this.ownerEntityId);
if (owner instanceof EntityClientGadget ownerGadget) {
this.originalOwnerEntityId = ownerGadget.getOriginalOwnerEntityId();
} else {
this.originalOwnerEntityId = this.ownerEntityId;
}
}
@Override
public void onDeath(int killerId) {
super.onDeath(killerId); // Invoke super class's onDeath() method.
}
@Override
public Int2FloatMap getFightProperties() {
return null;
}
@Override
public SceneEntityInfo toProto() {
EntityAuthorityInfo authority =
EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(
SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
.setBornPos(Vector.newBuilder())
.build();
SceneEntityInfo.Builder entityInfo =
SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setMotionInfo(
MotionInfo.newBuilder()
.setPos(getPosition().toProto())
.setRot(getRotation().toProto())
.setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(1);
PropPair pair =
PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
.build();
entityInfo.addPropList(pair);
ClientGadgetInfoOuterClass.ClientGadgetInfo clientGadget =
ClientGadgetInfoOuterClass.ClientGadgetInfo.newBuilder()
.setCampId(this.getCampId())
.setCampType(this.getCampType())
.setOwnerEntityId(this.getOwnerEntityId())
.setTargetEntityId(this.getTargetEntityId())
.setAsyncLoad(this.isAsyncLoad())
.build();
SceneGadgetInfo.Builder gadgetInfo =
SceneGadgetInfo.newBuilder()
.setGadgetId(this.getGadgetId())
.setOwnerEntityId(this.getOwnerEntityId())
.setIsEnableInteract(true)
.setClientGadget(clientGadget)
.setPropOwnerEntityId(this.getOwnerEntityId())
.setAuthorityPeerId(this.getOwner().getPeerId());
entityInfo.setGadget(gadgetInfo);
return entityInfo.build();
}
}
package emu.grasscutter.game.entity;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
import emu.grasscutter.net.proto.ClientGadgetInfoOuterClass;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData;
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
import emu.grasscutter.net.proto.EvtCreateGadgetNotifyOuterClass.EvtCreateGadgetNotify;
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType;
import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter;
public class EntityClientGadget extends EntityBaseGadget {
@Getter private final Player owner;
@Getter(onMethod_ = @Override)
private int gadgetId;
@Getter private int campId;
@Getter private int campType;
@Getter private int ownerEntityId;
@Getter private int targetEntityId;
@Getter private boolean asyncLoad;
@Getter private int originalOwnerEntityId;
public EntityClientGadget(Scene scene, Player player, EvtCreateGadgetNotify notify) {
super(scene, new Position(notify.getInitPos()), new Position(notify.getInitEulerAngles()));
this.owner = player;
this.id = notify.getEntityId();
this.gadgetId = notify.getConfigId();
this.campId = notify.getCampId();
this.campType = notify.getCampType();
this.ownerEntityId = notify.getPropOwnerEntityId();
this.targetEntityId = notify.getTargetEntityId();
this.asyncLoad = notify.getIsAsyncLoad();
GameEntity owner = scene.getEntityById(this.ownerEntityId);
if (owner instanceof EntityClientGadget ownerGadget) {
this.originalOwnerEntityId = ownerGadget.getOriginalOwnerEntityId();
} else {
this.originalOwnerEntityId = this.ownerEntityId;
}
}
@Override
public void onDeath(int killerId) {
super.onDeath(killerId); // Invoke super class's onDeath() method.
}
@Override
public Int2FloatMap getFightProperties() {
return null;
}
@Override
public SceneEntityInfo toProto() {
EntityAuthorityInfo authority =
EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(
SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
.setBornPos(Vector.newBuilder())
.build();
SceneEntityInfo.Builder entityInfo =
SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setMotionInfo(
MotionInfo.newBuilder()
.setPos(getPosition().toProto())
.setRot(getRotation().toProto())
.setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(1);
PropPair pair =
PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
.build();
entityInfo.addPropList(pair);
ClientGadgetInfoOuterClass.ClientGadgetInfo clientGadget =
ClientGadgetInfoOuterClass.ClientGadgetInfo.newBuilder()
.setCampId(this.getCampId())
.setCampType(this.getCampType())
.setOwnerEntityId(this.getOwnerEntityId())
.setTargetEntityId(this.getTargetEntityId())
.setAsyncLoad(this.isAsyncLoad())
.build();
SceneGadgetInfo.Builder gadgetInfo =
SceneGadgetInfo.newBuilder()
.setGadgetId(this.getGadgetId())
.setOwnerEntityId(this.getOwnerEntityId())
.setIsEnableInteract(true)
.setClientGadget(clientGadget)
.setPropOwnerEntityId(this.getOwnerEntityId())
.setAuthorityPeerId(this.getOwner().getPeerId());
entityInfo.setGadget(gadgetInfo);
return entityInfo.build();
}
}

View File

@ -66,7 +66,7 @@ public class EntityGadget extends EntityBaseGadget {
private final Int2FloatMap fightProperties = new Int2FloatOpenHashMap();
@Getter @Setter private SceneGadget metaGadget;
@Nullable @Getter private ConfigEntityGadget configGadget;
@Nullable @Getter ConfigEntityGadget configGadget;
@Getter @Setter private BaseRoute routeConfig;
@Getter @Setter private int stopValue = 0; // Controller related, inited to zero

View File

@ -42,7 +42,7 @@ public class EntityVehicle extends EntityBaseGadget {
@Getter private final int gadgetId;
@Getter @Setter private float curStamina;
@Getter private final List<VehicleMember> vehicleMembers;
@Getter private List<VehicleMember> vehicleMembers;
@Nullable @Getter private ConfigEntityGadget configGadget;
public EntityVehicle(

View File

@ -1,352 +1,352 @@
package emu.grasscutter.game.gacha;
import static emu.grasscutter.config.Configuration.*;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.GachaInfoOuterClass.GachaInfo;
import emu.grasscutter.net.proto.GachaUpInfoOuterClass.GachaUpInfo;
import emu.grasscutter.utils.Utils;
import lombok.Getter;
public class GachaBanner {
// Constants used by the BannerType enum
static final int[][] DEFAULT_WEIGHTS_4 = {{1, 510}, {8, 510}, {10, 10000}};
static final int[][] DEFAULT_WEIGHTS_4_WEAPON = {{1, 600}, {7, 600}, {8, 6600}, {10, 12600}};
static final int[][] DEFAULT_WEIGHTS_5 = {{1, 75}, {73, 150}, {90, 10000}};
static final int[][] DEFAULT_WEIGHTS_5_CHARACTER = {{1, 80}, {73, 80}, {90, 10000}};
static final int[][] DEFAULT_WEIGHTS_5_WEAPON = {{1, 100}, {62, 100}, {73, 7800}, {80, 10000}};
static final int[] DEFAULT_FALLBACK_ITEMS_4_POOL_1 = {
1014, 1020, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053,
1055, 1056, 1059, 1064, 1065, 1067, 1068, 1072
}; // Default avatars
static final int[] DEFAULT_FALLBACK_ITEMS_4_POOL_2 = {
11401, 11402, 11403, 11405, 12401, 12402, 12403, 12405, 13401, 13407, 14401, 14402, 14403,
14409, 15401, 15402, 15403, 15405
}; // Default weapons
static final int[] DEFAULT_FALLBACK_ITEMS_5_POOL_1 = {
1003, 1016, 1042, 1035, 1041, 1069
}; // Default avatars
static final int[] DEFAULT_FALLBACK_ITEMS_5_POOL_2 = {
11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502
}; // Default weapons
static final int[] EMPTY_POOL = {}; // Used to remove a type of fallback
@Getter int scheduleId = -1;
@Getter int sortId = -1;
@Getter private int gachaType = -1;
@Getter private String prefabPath;
@Getter private String previewPrefabPath;
@Getter private String titlePath;
private int costItemId = 0;
private final int costItemAmount = 1;
private int costItemId10 = 0;
private final int costItemAmount10 = 10;
@Getter private final int beginTime = 0;
@Getter private final int endTime = 1924992000;
@Getter private final int gachaTimesLimit = Integer.MAX_VALUE;
@Getter private final int[] rateUpItems4 = {};
@Getter private final int[] rateUpItems5 = {};
// This now handles default values for the fields below
@Getter private final BannerType bannerType = BannerType.STANDARD;
// These don't change between banner types (apart from Standard having three extra 4star avatars)
@Getter
private final int[] fallbackItems3 = {
11301, 11302, 11306, 12301, 12302, 12305, 13303, 14301, 14302, 14304, 15301, 15302, 15304
};
@Getter private final int[] fallbackItems4Pool1 = DEFAULT_FALLBACK_ITEMS_4_POOL_1;
@Getter private final int[] fallbackItems4Pool2 = DEFAULT_FALLBACK_ITEMS_4_POOL_2;
// Different banner types have different defaults, see above for default values and the enum for
// which are used where.
@Getter private int[] fallbackItems5Pool1;
@Getter private int[] fallbackItems5Pool2;
private int[][] weights4;
private int[][] weights5;
private int eventChance4 = -1; // Chance to win a featured event item
private int eventChance5 = -1; // Chance to win a featured event item
//
@Getter private final boolean removeC6FromPool = false;
@Getter
private final boolean autoStripRateUpFromFallback =
true; // Ensures that featured items won't "double dip" into the losing pool
private final int[][] poolBalanceWeights4 = {
{1, 255}, {17, 255}, {21, 10455}
}; // Used to ensure that players won't go too many rolls without getting something from pool 1
// (avatar) or pool 2 (weapon)
private final int[][] poolBalanceWeights5 = {{1, 30}, {147, 150}, {181, 10230}};
@Getter private final int wishMaxProgress = 2;
// Deprecated fields that were tolerated in early May 2022 but have apparently still being
// circulating in new custom configs
// For now, throw up big scary errors on load telling people that they will be banned outright in
// a future version
@Deprecated private final int[] rateUpItems1 = {};
@Deprecated private final int[] rateUpItems2 = {};
@Deprecated private final int eventChance = -1;
@Deprecated private final int costItem = 0;
@Deprecated private final int softPity = -1;
@Deprecated private final int hardPity = -1;
@Deprecated private final int minItemType = -1;
@Deprecated private final int maxItemType = -1;
@Getter private boolean deprecated = false;
@Getter private final boolean disabled = false;
private void warnDeprecated(String name, String replacement) {
Grasscutter.getLogger()
.error(
"Deprecated field found in Banners config: "
+ name
+ " was replaced back in early May 2022, use "
+ replacement
+ " instead. You MUST remove this field from your config.");
this.deprecated = true;
}
public void onLoad() {
// Handle deprecated configs
if (eventChance != -1) warnDeprecated("eventChance", "eventChance4 & eventChance5");
if (costItem != 0) warnDeprecated("costItem", "costItemId");
if (softPity != -1) warnDeprecated("softPity", "weights5");
if (hardPity != -1) warnDeprecated("hardPity", "weights5");
if (minItemType != -1) warnDeprecated("minItemType", "fallbackItems[4,5]Pool[1,2]");
if (maxItemType != -1) warnDeprecated("maxItemType", "fallbackItems[4,5]Pool[1,2]");
if (rateUpItems1.length > 0) warnDeprecated("rateUpItems1", "rateUpItems5");
if (rateUpItems2.length > 0) warnDeprecated("rateUpItems2", "rateUpItems4");
// Handle default values
if (this.previewPrefabPath != null
&& this.previewPrefabPath.equals("UI_Tab_" + this.prefabPath))
Grasscutter.getLogger()
.error(
"Redundant field found in Banners config: previewPrefabPath does not need to be specified if it is identical to prefabPath prefixed with \"UI_Tab_\".");
if (this.previewPrefabPath == null || this.previewPrefabPath.isEmpty())
this.previewPrefabPath = "UI_Tab_" + this.prefabPath;
if (this.gachaType < 0) this.gachaType = this.bannerType.gachaType;
if (this.costItemId == 0) this.costItemId = this.bannerType.costItemId;
if (this.costItemId10 == 0) this.costItemId10 = this.costItemId;
if (this.weights4 == null) this.weights4 = this.bannerType.weights4;
if (this.weights5 == null) this.weights5 = this.bannerType.weights5;
if (this.eventChance4 < 0) this.eventChance4 = this.bannerType.eventChance4;
if (this.eventChance5 < 0) this.eventChance5 = this.bannerType.eventChance5;
if (this.fallbackItems5Pool1 == null)
this.fallbackItems5Pool1 = this.bannerType.fallbackItems5Pool1;
if (this.fallbackItems5Pool2 == null)
this.fallbackItems5Pool2 = this.bannerType.fallbackItems5Pool2;
}
public ItemParamData getCost(int numRolls) {
return switch (numRolls) {
case 10 -> new ItemParamData(costItemId10, costItemAmount10);
default -> new ItemParamData(costItemId, costItemAmount * numRolls);
};
}
@Deprecated
public int getCostItem() {
return costItemId;
}
public boolean hasEpitomized() {
return bannerType.equals(BannerType.WEAPON);
}
public int getWeight(int rarity, int pity) {
return switch (rarity) {
case 4 -> Utils.lerp(pity, weights4);
default -> Utils.lerp(pity, weights5);
};
}
public int getPoolBalanceWeight(int rarity, int pity) {
return switch (rarity) {
case 4 -> Utils.lerp(pity, poolBalanceWeights4);
default -> Utils.lerp(pity, poolBalanceWeights5);
};
}
public int getEventChance(int rarity) {
return switch (rarity) {
case 4 -> eventChance4;
default -> eventChance5;
};
}
public GachaInfo toProto(Player player) {
// TODO: use other Nonce/key insteadof session key to ensure the overall security for the player
String sessionKey = player.getAccount().getSessionKey();
String record =
"http"
+ (HTTP_ENCRYPTION.useInRouting ? "s" : "")
+ "://"
+ lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress)
+ ":"
+ lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort)
+ "/gacha?s="
+ sessionKey
+ "&gachaType="
+ gachaType;
String details =
"http"
+ (HTTP_ENCRYPTION.useInRouting ? "s" : "")
+ "://"
+ lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress)
+ ":"
+ lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort)
+ "/gacha/details?s="
+ sessionKey
+ "&scheduleId="
+ scheduleId;
// Grasscutter.getLogger().info("record = " + record);
PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(this);
int leftGachaTimes =
switch (gachaTimesLimit) {
case Integer.MAX_VALUE -> Integer.MAX_VALUE;
default -> Math.max(gachaTimesLimit - gachaInfo.getTotalPulls(), 0);
};
GachaInfo.Builder info =
GachaInfo.newBuilder()
.setGachaType(this.getGachaType())
.setScheduleId(this.getScheduleId())
.setBeginTime(this.getBeginTime())
.setEndTime(this.getEndTime())
.setCostItemId(this.costItemId)
.setCostItemNum(this.costItemAmount)
.setTenCostItemId(this.costItemId10)
.setTenCostItemNum(this.costItemAmount10)
.setGachaPrefabPath(this.getPrefabPath())
.setGachaPreviewPrefabPath(this.getPreviewPrefabPath())
.setGachaProbUrl(details)
.setGachaProbUrlOversea(details)
.setGachaRecordUrl(record)
.setGachaRecordUrlOversea(record)
.setLeftGachaTimes(leftGachaTimes)
.setGachaTimesLimit(gachaTimesLimit)
.setGachaSortId(this.getSortId());
if (hasEpitomized()) {
info.setWishItemId(gachaInfo.getWishItemId())
.setWishProgress(gachaInfo.getFailedChosenItemPulls())
.setWishMaxProgress(this.getWishMaxProgress());
}
if (this.getTitlePath() != null) {
info.setTitleTextmap(this.getTitlePath());
}
if (this.getRateUpItems5().length > 0) {
GachaUpInfo.Builder upInfo = GachaUpInfo.newBuilder().setItemParentType(1);
for (int id : getRateUpItems5()) {
upInfo.addItemIdList(id);
info.addDisplayUp5ItemList(id);
}
info.addGachaUpInfoList(upInfo);
}
if (this.getRateUpItems4().length > 0) {
GachaUpInfo.Builder upInfo = GachaUpInfo.newBuilder().setItemParentType(2);
for (int id : getRateUpItems4()) {
upInfo.addItemIdList(id);
if (info.getDisplayUp4ItemListCount() == 0) {
info.addDisplayUp4ItemList(id);
}
}
info.addGachaUpInfoList(upInfo);
}
return info.build();
}
public enum BannerType {
STANDARD(
200,
224,
DEFAULT_WEIGHTS_4,
DEFAULT_WEIGHTS_5,
50,
50,
DEFAULT_FALLBACK_ITEMS_5_POOL_1,
DEFAULT_FALLBACK_ITEMS_5_POOL_2),
BEGINNER(
100,
224,
DEFAULT_WEIGHTS_4,
DEFAULT_WEIGHTS_5,
50,
50,
DEFAULT_FALLBACK_ITEMS_5_POOL_1,
DEFAULT_FALLBACK_ITEMS_5_POOL_2),
EVENT(
301,
223,
DEFAULT_WEIGHTS_4,
DEFAULT_WEIGHTS_5_CHARACTER,
50,
50,
DEFAULT_FALLBACK_ITEMS_5_POOL_1,
DEFAULT_FALLBACK_ITEMS_5_POOL_2), // Legacy value for CHARACTER
CHARACTER(
301,
223,
DEFAULT_WEIGHTS_4,
DEFAULT_WEIGHTS_5_CHARACTER,
50,
50,
DEFAULT_FALLBACK_ITEMS_5_POOL_1,
EMPTY_POOL),
CHARACTER2(
400,
223,
DEFAULT_WEIGHTS_4,
DEFAULT_WEIGHTS_5_CHARACTER,
50,
50,
DEFAULT_FALLBACK_ITEMS_5_POOL_1,
EMPTY_POOL),
WEAPON(
302,
223,
DEFAULT_WEIGHTS_4_WEAPON,
DEFAULT_WEIGHTS_5_WEAPON,
75,
75,
EMPTY_POOL,
DEFAULT_FALLBACK_ITEMS_5_POOL_2);
public final int gachaType;
public final int costItemId;
public final int[][] weights4;
public final int[][] weights5;
public final int eventChance4;
public final int eventChance5;
public final int[] fallbackItems5Pool1;
public final int[] fallbackItems5Pool2;
BannerType(
int gachaType,
int costItemId,
int[][] weights4,
int[][] weights5,
int eventChance4,
int eventChance5,
int[] fallbackItems5Pool1,
int[] fallbackItems5Pool2) {
this.gachaType = gachaType;
this.costItemId = costItemId;
this.weights4 = weights4;
this.weights5 = weights5;
this.eventChance4 = eventChance4;
this.eventChance5 = eventChance5;
this.fallbackItems5Pool1 = fallbackItems5Pool1;
this.fallbackItems5Pool2 = fallbackItems5Pool2;
}
}
}
package emu.grasscutter.game.gacha;
import static emu.grasscutter.config.Configuration.*;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.GachaInfoOuterClass.GachaInfo;
import emu.grasscutter.net.proto.GachaUpInfoOuterClass.GachaUpInfo;
import emu.grasscutter.utils.Utils;
import lombok.Getter;
public class GachaBanner {
// Constants used by the BannerType enum
static final int[][] DEFAULT_WEIGHTS_4 = {{1, 510}, {8, 510}, {10, 10000}};
static final int[][] DEFAULT_WEIGHTS_4_WEAPON = {{1, 600}, {7, 600}, {8, 6600}, {10, 12600}};
static final int[][] DEFAULT_WEIGHTS_5 = {{1, 75}, {73, 150}, {90, 10000}};
static final int[][] DEFAULT_WEIGHTS_5_CHARACTER = {{1, 80}, {73, 80}, {90, 10000}};
static final int[][] DEFAULT_WEIGHTS_5_WEAPON = {{1, 100}, {62, 100}, {73, 7800}, {80, 10000}};
static final int[] DEFAULT_FALLBACK_ITEMS_4_POOL_1 = {
1014, 1020, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053,
1055, 1056, 1059, 1064, 1065, 1067, 1068, 1072
}; // Default avatars
static final int[] DEFAULT_FALLBACK_ITEMS_4_POOL_2 = {
11401, 11402, 11403, 11405, 12401, 12402, 12403, 12405, 13401, 13407, 14401, 14402, 14403,
14409, 15401, 15402, 15403, 15405
}; // Default weapons
static final int[] DEFAULT_FALLBACK_ITEMS_5_POOL_1 = {
1003, 1016, 1042, 1035, 1041, 1069
}; // Default avatars
static final int[] DEFAULT_FALLBACK_ITEMS_5_POOL_2 = {
11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502
}; // Default weapons
static final int[] EMPTY_POOL = {}; // Used to remove a type of fallback
@Getter int scheduleId = -1;
@Getter int sortId = -1;
@Getter private int gachaType = -1;
@Getter private String prefabPath;
@Getter private String previewPrefabPath;
@Getter private String titlePath;
private int costItemId = 0;
private int costItemAmount = 1;
private int costItemId10 = 0;
private int costItemAmount10 = 10;
@Getter private int beginTime = 0;
@Getter private int endTime = 1924992000;
@Getter private int gachaTimesLimit = Integer.MAX_VALUE;
@Getter private int[] rateUpItems4 = {};
@Getter private int[] rateUpItems5 = {};
// This now handles default values for the fields below
@Getter private BannerType bannerType = BannerType.STANDARD;
// These don't change between banner types (apart from Standard having three extra 4star avatars)
@Getter
private int[] fallbackItems3 = {
11301, 11302, 11306, 12301, 12302, 12305, 13303, 14301, 14302, 14304, 15301, 15302, 15304
};
@Getter private int[] fallbackItems4Pool1 = DEFAULT_FALLBACK_ITEMS_4_POOL_1;
@Getter private int[] fallbackItems4Pool2 = DEFAULT_FALLBACK_ITEMS_4_POOL_2;
// Different banner types have different defaults, see above for default values and the enum for
// which are used where.
@Getter private int[] fallbackItems5Pool1;
@Getter private int[] fallbackItems5Pool2;
private int[][] weights4;
private int[][] weights5;
private int eventChance4 = -1; // Chance to win a featured event item
private int eventChance5 = -1; // Chance to win a featured event item
//
@Getter private boolean removeC6FromPool = false;
@Getter
private boolean autoStripRateUpFromFallback =
true; // Ensures that featured items won't "double dip" into the losing pool
private int[][] poolBalanceWeights4 = {
{1, 255}, {17, 255}, {21, 10455}
}; // Used to ensure that players won't go too many rolls without getting something from pool 1
// (avatar) or pool 2 (weapon)
private int[][] poolBalanceWeights5 = {{1, 30}, {147, 150}, {181, 10230}};
@Getter private int wishMaxProgress = 2;
// Deprecated fields that were tolerated in early May 2022 but have apparently still being
// circulating in new custom configs
// For now, throw up big scary errors on load telling people that they will be banned outright in
// a future version
@Deprecated private int[] rateUpItems1 = {};
@Deprecated private int[] rateUpItems2 = {};
@Deprecated private int eventChance = -1;
@Deprecated private int costItem = 0;
@Deprecated private int softPity = -1;
@Deprecated private int hardPity = -1;
@Deprecated private int minItemType = -1;
@Deprecated private int maxItemType = -1;
@Getter private boolean deprecated = false;
@Getter private boolean disabled = false;
private void warnDeprecated(String name, String replacement) {
Grasscutter.getLogger()
.error(
"Deprecated field found in Banners config: "
+ name
+ " was replaced back in early May 2022, use "
+ replacement
+ " instead. You MUST remove this field from your config.");
this.deprecated = true;
}
public void onLoad() {
// Handle deprecated configs
if (eventChance != -1) warnDeprecated("eventChance", "eventChance4 & eventChance5");
if (costItem != 0) warnDeprecated("costItem", "costItemId");
if (softPity != -1) warnDeprecated("softPity", "weights5");
if (hardPity != -1) warnDeprecated("hardPity", "weights5");
if (minItemType != -1) warnDeprecated("minItemType", "fallbackItems[4,5]Pool[1,2]");
if (maxItemType != -1) warnDeprecated("maxItemType", "fallbackItems[4,5]Pool[1,2]");
if (rateUpItems1.length > 0) warnDeprecated("rateUpItems1", "rateUpItems5");
if (rateUpItems2.length > 0) warnDeprecated("rateUpItems2", "rateUpItems4");
// Handle default values
if (this.previewPrefabPath != null
&& this.previewPrefabPath.equals("UI_Tab_" + this.prefabPath))
Grasscutter.getLogger()
.error(
"Redundant field found in Banners config: previewPrefabPath does not need to be specified if it is identical to prefabPath prefixed with \"UI_Tab_\".");
if (this.previewPrefabPath == null || this.previewPrefabPath.isEmpty())
this.previewPrefabPath = "UI_Tab_" + this.prefabPath;
if (this.gachaType < 0) this.gachaType = this.bannerType.gachaType;
if (this.costItemId == 0) this.costItemId = this.bannerType.costItemId;
if (this.costItemId10 == 0) this.costItemId10 = this.costItemId;
if (this.weights4 == null) this.weights4 = this.bannerType.weights4;
if (this.weights5 == null) this.weights5 = this.bannerType.weights5;
if (this.eventChance4 < 0) this.eventChance4 = this.bannerType.eventChance4;
if (this.eventChance5 < 0) this.eventChance5 = this.bannerType.eventChance5;
if (this.fallbackItems5Pool1 == null)
this.fallbackItems5Pool1 = this.bannerType.fallbackItems5Pool1;
if (this.fallbackItems5Pool2 == null)
this.fallbackItems5Pool2 = this.bannerType.fallbackItems5Pool2;
}
public ItemParamData getCost(int numRolls) {
return switch (numRolls) {
case 10 -> new ItemParamData(costItemId10, costItemAmount10);
default -> new ItemParamData(costItemId, costItemAmount * numRolls);
};
}
@Deprecated
public int getCostItem() {
return costItemId;
}
public boolean hasEpitomized() {
return bannerType.equals(BannerType.WEAPON);
}
public int getWeight(int rarity, int pity) {
return switch (rarity) {
case 4 -> Utils.lerp(pity, weights4);
default -> Utils.lerp(pity, weights5);
};
}
public int getPoolBalanceWeight(int rarity, int pity) {
return switch (rarity) {
case 4 -> Utils.lerp(pity, poolBalanceWeights4);
default -> Utils.lerp(pity, poolBalanceWeights5);
};
}
public int getEventChance(int rarity) {
return switch (rarity) {
case 4 -> eventChance4;
default -> eventChance5;
};
}
public GachaInfo toProto(Player player) {
// TODO: use other Nonce/key insteadof session key to ensure the overall security for the player
String sessionKey = player.getAccount().getSessionKey();
String record =
"http"
+ (HTTP_ENCRYPTION.useInRouting ? "s" : "")
+ "://"
+ lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress)
+ ":"
+ lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort)
+ "/gacha?s="
+ sessionKey
+ "&gachaType="
+ gachaType;
String details =
"http"
+ (HTTP_ENCRYPTION.useInRouting ? "s" : "")
+ "://"
+ lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress)
+ ":"
+ lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort)
+ "/gacha/details?s="
+ sessionKey
+ "&scheduleId="
+ scheduleId;
// Grasscutter.getLogger().info("record = " + record);
PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(this);
int leftGachaTimes =
switch (gachaTimesLimit) {
case Integer.MAX_VALUE -> Integer.MAX_VALUE;
default -> Math.max(gachaTimesLimit - gachaInfo.getTotalPulls(), 0);
};
GachaInfo.Builder info =
GachaInfo.newBuilder()
.setGachaType(this.getGachaType())
.setScheduleId(this.getScheduleId())
.setBeginTime(this.getBeginTime())
.setEndTime(this.getEndTime())
.setCostItemId(this.costItemId)
.setCostItemNum(this.costItemAmount)
.setTenCostItemId(this.costItemId10)
.setTenCostItemNum(this.costItemAmount10)
.setGachaPrefabPath(this.getPrefabPath())
.setGachaPreviewPrefabPath(this.getPreviewPrefabPath())
.setGachaProbUrl(details)
.setGachaProbUrlOversea(details)
.setGachaRecordUrl(record)
.setGachaRecordUrlOversea(record)
.setLeftGachaTimes(leftGachaTimes)
.setGachaTimesLimit(gachaTimesLimit)
.setGachaSortId(this.getSortId());
if (hasEpitomized()) {
info.setWishItemId(gachaInfo.getWishItemId())
.setWishProgress(gachaInfo.getFailedChosenItemPulls())
.setWishMaxProgress(this.getWishMaxProgress());
}
if (this.getTitlePath() != null) {
info.setTitleTextmap(this.getTitlePath());
}
if (this.getRateUpItems5().length > 0) {
GachaUpInfo.Builder upInfo = GachaUpInfo.newBuilder().setItemParentType(1);
for (int id : getRateUpItems5()) {
upInfo.addItemIdList(id);
info.addDisplayUp5ItemList(id);
}
info.addGachaUpInfoList(upInfo);
}
if (this.getRateUpItems4().length > 0) {
GachaUpInfo.Builder upInfo = GachaUpInfo.newBuilder().setItemParentType(2);
for (int id : getRateUpItems4()) {
upInfo.addItemIdList(id);
if (info.getDisplayUp4ItemListCount() == 0) {
info.addDisplayUp4ItemList(id);
}
}
info.addGachaUpInfoList(upInfo);
}
return info.build();
}
public enum BannerType {
STANDARD(
200,
224,
DEFAULT_WEIGHTS_4,
DEFAULT_WEIGHTS_5,
50,
50,
DEFAULT_FALLBACK_ITEMS_5_POOL_1,
DEFAULT_FALLBACK_ITEMS_5_POOL_2),
BEGINNER(
100,
224,
DEFAULT_WEIGHTS_4,
DEFAULT_WEIGHTS_5,
50,
50,
DEFAULT_FALLBACK_ITEMS_5_POOL_1,
DEFAULT_FALLBACK_ITEMS_5_POOL_2),
EVENT(
301,
223,
DEFAULT_WEIGHTS_4,
DEFAULT_WEIGHTS_5_CHARACTER,
50,
50,
DEFAULT_FALLBACK_ITEMS_5_POOL_1,
DEFAULT_FALLBACK_ITEMS_5_POOL_2), // Legacy value for CHARACTER
CHARACTER(
301,
223,
DEFAULT_WEIGHTS_4,
DEFAULT_WEIGHTS_5_CHARACTER,
50,
50,
DEFAULT_FALLBACK_ITEMS_5_POOL_1,
EMPTY_POOL),
CHARACTER2(
400,
223,
DEFAULT_WEIGHTS_4,
DEFAULT_WEIGHTS_5_CHARACTER,
50,
50,
DEFAULT_FALLBACK_ITEMS_5_POOL_1,
EMPTY_POOL),
WEAPON(
302,
223,
DEFAULT_WEIGHTS_4_WEAPON,
DEFAULT_WEIGHTS_5_WEAPON,
75,
75,
EMPTY_POOL,
DEFAULT_FALLBACK_ITEMS_5_POOL_2);
public final int gachaType;
public final int costItemId;
public final int[][] weights4;
public final int[][] weights5;
public final int eventChance4;
public final int eventChance5;
public final int[] fallbackItems5Pool1;
public final int[] fallbackItems5Pool2;
BannerType(
int gachaType,
int costItemId,
int[][] weights4,
int[][] weights5,
int eventChance4,
int eventChance5,
int[] fallbackItems5Pool1,
int[] fallbackItems5Pool2) {
this.gachaType = gachaType;
this.costItemId = costItemId;
this.weights4 = weights4;
this.weights5 = weights5;
this.eventChance4 = eventChance4;
this.eventChance5 = eventChance5;
this.fallbackItems5Pool1 = fallbackItems5Pool1;
this.fallbackItems5Pool2 = fallbackItems5Pool2;
}
}
}

View File

@ -1,62 +1,62 @@
package emu.grasscutter.game.managers.mapmark;
import dev.morphia.annotations.Entity;
import emu.grasscutter.net.proto.MapMarkFromTypeOuterClass.MapMarkFromType;
import emu.grasscutter.net.proto.MapMarkPointOuterClass.MapMarkPoint;
import emu.grasscutter.net.proto.MapMarkPointTypeOuterClass.MapMarkPointType;
import emu.grasscutter.utils.Position;
@Entity
public class MapMark {
private int sceneId;
private String name;
private Position position;
private final MapMarkPointType mapMarkPointType;
private int monsterId;
private final MapMarkFromType mapMarkFromType;
private int questId;
@Deprecated // Morhpia
public MapMark() {
this.mapMarkPointType = MapMarkPointType.MAP_MARK_POINT_TYPE_MONSTER;
this.mapMarkFromType = MapMarkFromType.MAP_MARK_FROM_TYPE_MONSTER;
}
public MapMark(MapMarkPoint mapMarkPoint) {
this.sceneId = mapMarkPoint.getSceneId();
this.name = mapMarkPoint.getName();
this.position = new Position(mapMarkPoint.getPos());
this.mapMarkPointType = mapMarkPoint.getPointType();
this.monsterId = mapMarkPoint.getMonsterId();
this.mapMarkFromType = mapMarkPoint.getFromType();
this.questId = mapMarkPoint.getQuestId();
}
public int getSceneId() {
return this.sceneId;
}
public String getName() {
return this.name;
}
public Position getPosition() {
return this.position;
}
public MapMarkPointType getMapMarkPointType() {
return this.mapMarkPointType;
}
public int getMonsterId() {
return this.monsterId;
}
public MapMarkFromType getMapMarkFromType() {
return this.mapMarkFromType;
}
public int getQuestId() {
return this.questId;
}
}
package emu.grasscutter.game.managers.mapmark;
import dev.morphia.annotations.Entity;
import emu.grasscutter.net.proto.MapMarkFromTypeOuterClass.MapMarkFromType;
import emu.grasscutter.net.proto.MapMarkPointOuterClass.MapMarkPoint;
import emu.grasscutter.net.proto.MapMarkPointTypeOuterClass.MapMarkPointType;
import emu.grasscutter.utils.Position;
@Entity
public class MapMark {
private int sceneId;
private String name;
private Position position;
private MapMarkPointType mapMarkPointType;
private int monsterId;
private MapMarkFromType mapMarkFromType;
private int questId;
@Deprecated // Morhpia
public MapMark() {
this.mapMarkPointType = MapMarkPointType.MAP_MARK_POINT_TYPE_MONSTER;
this.mapMarkFromType = MapMarkFromType.MAP_MARK_FROM_TYPE_MONSTER;
}
public MapMark(MapMarkPoint mapMarkPoint) {
this.sceneId = mapMarkPoint.getSceneId();
this.name = mapMarkPoint.getName();
this.position = new Position(mapMarkPoint.getPos());
this.mapMarkPointType = mapMarkPoint.getPointType();
this.monsterId = mapMarkPoint.getMonsterId();
this.mapMarkFromType = mapMarkPoint.getFromType();
this.questId = mapMarkPoint.getQuestId();
}
public int getSceneId() {
return this.sceneId;
}
public String getName() {
return this.name;
}
public Position getPosition() {
return this.position;
}
public MapMarkPointType getMapMarkPointType() {
return this.mapMarkPointType;
}
public int getMonsterId() {
return this.monsterId;
}
public MapMarkFromType getMapMarkFromType() {
return this.mapMarkFromType;
}
public int getQuestId() {
return this.questId;
}
}

View File

@ -17,14 +17,14 @@ public class PlayerCodex {
@Transient private Player player;
// itemId is not codexId!
@Getter private final Set<Integer> unlockedWeapon;
@Getter private final Map<Integer, Integer> unlockedAnimal;
@Getter private final Set<Integer> unlockedMaterial;
@Getter private final Set<Integer> unlockedBook;
@Getter private final Set<Integer> unlockedTip;
@Getter private final Set<Integer> unlockedView;
@Getter private Set<Integer> unlockedWeapon;
@Getter private Map<Integer, Integer> unlockedAnimal;
@Getter private Set<Integer> unlockedMaterial;
@Getter private Set<Integer> unlockedBook;
@Getter private Set<Integer> unlockedTip;
@Getter private Set<Integer> unlockedView;
@Getter private Set<Integer> unlockedReliquary;
@Getter private final Set<Integer> unlockedReliquarySuitCodex;
@Getter private Set<Integer> unlockedReliquarySuitCodex;
public PlayerCodex() {
this.unlockedWeapon = new HashSet<>();

View File

@ -11,7 +11,7 @@ import java.util.List;
@Entity
public final class TeamInfo {
private String name;
private final List<Integer> avatars;
private List<Integer> avatars;
public TeamInfo() {
this.name = "";

View File

@ -1,263 +1,263 @@
package emu.grasscutter.game.props;
import static java.util.Map.entry;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Stream;
import lombok.Getter;
public enum FightProperty {
FIGHT_PROP_NONE(0),
FIGHT_PROP_BASE_HP(1),
FIGHT_PROP_HP(2),
FIGHT_PROP_HP_PERCENT(3),
FIGHT_PROP_BASE_ATTACK(4),
FIGHT_PROP_ATTACK(5),
FIGHT_PROP_ATTACK_PERCENT(6),
FIGHT_PROP_BASE_DEFENSE(7),
FIGHT_PROP_DEFENSE(8),
FIGHT_PROP_DEFENSE_PERCENT(9),
FIGHT_PROP_BASE_SPEED(10),
FIGHT_PROP_SPEED_PERCENT(11),
FIGHT_PROP_HP_MP_PERCENT(12),
FIGHT_PROP_ATTACK_MP_PERCENT(13),
FIGHT_PROP_CRITICAL(20),
FIGHT_PROP_ANTI_CRITICAL(21),
FIGHT_PROP_CRITICAL_HURT(22),
FIGHT_PROP_CHARGE_EFFICIENCY(23),
FIGHT_PROP_ADD_HURT(24),
FIGHT_PROP_SUB_HURT(25),
FIGHT_PROP_HEAL_ADD(26),
FIGHT_PROP_HEALED_ADD(27),
FIGHT_PROP_ELEMENT_MASTERY(28),
FIGHT_PROP_PHYSICAL_SUB_HURT(29),
FIGHT_PROP_PHYSICAL_ADD_HURT(30),
FIGHT_PROP_DEFENCE_IGNORE_RATIO(31),
FIGHT_PROP_DEFENCE_IGNORE_DELTA(32),
FIGHT_PROP_FIRE_ADD_HURT(40),
FIGHT_PROP_ELEC_ADD_HURT(41),
FIGHT_PROP_WATER_ADD_HURT(42),
FIGHT_PROP_GRASS_ADD_HURT(43),
FIGHT_PROP_WIND_ADD_HURT(44),
FIGHT_PROP_ROCK_ADD_HURT(45),
FIGHT_PROP_ICE_ADD_HURT(46),
FIGHT_PROP_HIT_HEAD_ADD_HURT(47),
FIGHT_PROP_FIRE_SUB_HURT(50),
FIGHT_PROP_ELEC_SUB_HURT(51),
FIGHT_PROP_WATER_SUB_HURT(52),
FIGHT_PROP_GRASS_SUB_HURT(53),
FIGHT_PROP_WIND_SUB_HURT(54),
FIGHT_PROP_ROCK_SUB_HURT(55),
FIGHT_PROP_ICE_SUB_HURT(56),
FIGHT_PROP_EFFECT_HIT(60),
FIGHT_PROP_EFFECT_RESIST(61),
FIGHT_PROP_FREEZE_RESIST(62),
FIGHT_PROP_TORPOR_RESIST(63),
FIGHT_PROP_DIZZY_RESIST(64),
FIGHT_PROP_FREEZE_SHORTEN(65),
FIGHT_PROP_TORPOR_SHORTEN(66),
FIGHT_PROP_DIZZY_SHORTEN(67),
FIGHT_PROP_MAX_FIRE_ENERGY(70),
FIGHT_PROP_MAX_ELEC_ENERGY(71),
FIGHT_PROP_MAX_WATER_ENERGY(72),
FIGHT_PROP_MAX_GRASS_ENERGY(73),
FIGHT_PROP_MAX_WIND_ENERGY(74),
FIGHT_PROP_MAX_ICE_ENERGY(75),
FIGHT_PROP_MAX_ROCK_ENERGY(76),
FIGHT_PROP_SKILL_CD_MINUS_RATIO(80),
FIGHT_PROP_SHIELD_COST_MINUS_RATIO(81),
FIGHT_PROP_CUR_FIRE_ENERGY(1000),
FIGHT_PROP_CUR_ELEC_ENERGY(1001),
FIGHT_PROP_CUR_WATER_ENERGY(1002),
FIGHT_PROP_CUR_GRASS_ENERGY(1003),
FIGHT_PROP_CUR_WIND_ENERGY(1004),
FIGHT_PROP_CUR_ICE_ENERGY(1005),
FIGHT_PROP_CUR_ROCK_ENERGY(1006),
FIGHT_PROP_CUR_HP(1010),
FIGHT_PROP_MAX_HP(2000),
FIGHT_PROP_CUR_ATTACK(2001),
FIGHT_PROP_CUR_DEFENSE(2002),
FIGHT_PROP_CUR_SPEED(2003),
FIGHT_PROP_NONEXTRA_ATTACK(3000),
FIGHT_PROP_NONEXTRA_DEFENSE(3001),
FIGHT_PROP_NONEXTRA_CRITICAL(3002),
FIGHT_PROP_NONEXTRA_ANTI_CRITICAL(3003),
FIGHT_PROP_NONEXTRA_CRITICAL_HURT(3004),
FIGHT_PROP_NONEXTRA_CHARGE_EFFICIENCY(3005),
FIGHT_PROP_NONEXTRA_ELEMENT_MASTERY(3006),
FIGHT_PROP_NONEXTRA_PHYSICAL_SUB_HURT(3007),
FIGHT_PROP_NONEXTRA_FIRE_ADD_HURT(3008),
FIGHT_PROP_NONEXTRA_ELEC_ADD_HURT(3009),
FIGHT_PROP_NONEXTRA_WATER_ADD_HURT(3010),
FIGHT_PROP_NONEXTRA_GRASS_ADD_HURT(3011),
FIGHT_PROP_NONEXTRA_WIND_ADD_HURT(3012),
FIGHT_PROP_NONEXTRA_ROCK_ADD_HURT(3013),
FIGHT_PROP_NONEXTRA_ICE_ADD_HURT(3014),
FIGHT_PROP_NONEXTRA_FIRE_SUB_HURT(3015),
FIGHT_PROP_NONEXTRA_ELEC_SUB_HURT(3016),
FIGHT_PROP_NONEXTRA_WATER_SUB_HURT(3017),
FIGHT_PROP_NONEXTRA_GRASS_SUB_HURT(3018),
FIGHT_PROP_NONEXTRA_WIND_SUB_HURT(3019),
FIGHT_PROP_NONEXTRA_ROCK_SUB_HURT(3020),
FIGHT_PROP_NONEXTRA_ICE_SUB_HURT(3021),
FIGHT_PROP_NONEXTRA_SKILL_CD_MINUS_RATIO(3022),
FIGHT_PROP_NONEXTRA_SHIELD_COST_MINUS_RATIO(3023),
FIGHT_PROP_NONEXTRA_PHYSICAL_ADD_HURT(3024);
public static final int[] fightProps =
new int[] {
1, 4, 7, 20, 21, 22, 23, 26, 27, 28, 29, 30, 40, 41, 42, 43, 44, 45, 46, 50, 51, 52, 53, 54,
55, 56, 2000, 2001, 2002, 2003, 1010
};
private static final Int2ObjectMap<FightProperty> map = new Int2ObjectOpenHashMap<>();
private static final Map<String, FightProperty> stringMap = new HashMap<>();
// This was originally for relic properties so some names might not be applicable for e.g.
// setstats
private static final Map<String, FightProperty> shortNameMap =
Map.ofEntries(
// Normal relic stats
entry("hp", FIGHT_PROP_HP),
entry("atk", FIGHT_PROP_ATTACK),
entry("def", FIGHT_PROP_DEFENSE),
entry("hp%", FIGHT_PROP_HP_PERCENT),
entry("atk%", FIGHT_PROP_ATTACK_PERCENT),
entry("def%", FIGHT_PROP_DEFENSE_PERCENT),
entry("em", FIGHT_PROP_ELEMENT_MASTERY),
entry("er", FIGHT_PROP_CHARGE_EFFICIENCY),
entry("hb", FIGHT_PROP_HEAL_ADD),
entry("heal", FIGHT_PROP_HEAL_ADD),
entry("cd", FIGHT_PROP_CRITICAL_HURT),
entry("cdmg", FIGHT_PROP_CRITICAL_HURT),
entry("cr", FIGHT_PROP_CRITICAL),
entry("crate", FIGHT_PROP_CRITICAL),
entry("phys%", FIGHT_PROP_PHYSICAL_ADD_HURT),
entry("dendro%", FIGHT_PROP_GRASS_ADD_HURT),
entry("geo%", FIGHT_PROP_ROCK_ADD_HURT),
entry("anemo%", FIGHT_PROP_WIND_ADD_HURT),
entry("hydro%", FIGHT_PROP_WATER_ADD_HURT),
entry("cryo%", FIGHT_PROP_ICE_ADD_HURT),
entry("electro%", FIGHT_PROP_ELEC_ADD_HURT),
entry("pyro%", FIGHT_PROP_FIRE_ADD_HURT),
// Other stats
entry("maxhp", FIGHT_PROP_MAX_HP),
entry("dmg", FIGHT_PROP_ADD_HURT), // This seems to get reset after attacks
entry("cdr", FIGHT_PROP_SKILL_CD_MINUS_RATIO),
entry("heali", FIGHT_PROP_HEALED_ADD),
entry("shield", FIGHT_PROP_SHIELD_COST_MINUS_RATIO),
entry("defi", FIGHT_PROP_DEFENCE_IGNORE_RATIO),
entry("resall", FIGHT_PROP_SUB_HURT), // This seems to get reset after attacks
entry("resanemo", FIGHT_PROP_WIND_SUB_HURT),
entry("rescryo", FIGHT_PROP_ICE_SUB_HURT),
entry("resdendro", FIGHT_PROP_GRASS_SUB_HURT),
entry("reselectro", FIGHT_PROP_ELEC_SUB_HURT),
entry("resgeo", FIGHT_PROP_ROCK_SUB_HURT),
entry("reshydro", FIGHT_PROP_WATER_SUB_HURT),
entry("respyro", FIGHT_PROP_FIRE_SUB_HURT),
entry("resphys", FIGHT_PROP_PHYSICAL_SUB_HURT));
private static final List<FightProperty> flatProps =
Arrays.asList(
FIGHT_PROP_BASE_HP,
FIGHT_PROP_HP,
FIGHT_PROP_BASE_ATTACK,
FIGHT_PROP_ATTACK,
FIGHT_PROP_BASE_DEFENSE,
FIGHT_PROP_DEFENSE,
FIGHT_PROP_HEALED_ADD,
FIGHT_PROP_CUR_FIRE_ENERGY,
FIGHT_PROP_CUR_ELEC_ENERGY,
FIGHT_PROP_CUR_WATER_ENERGY,
FIGHT_PROP_CUR_GRASS_ENERGY,
FIGHT_PROP_CUR_WIND_ENERGY,
FIGHT_PROP_CUR_ICE_ENERGY,
FIGHT_PROP_CUR_ROCK_ENERGY,
FIGHT_PROP_CUR_HP,
FIGHT_PROP_MAX_HP,
FIGHT_PROP_CUR_ATTACK,
FIGHT_PROP_CUR_DEFENSE);
private static final Map<FightProperty, CompoundProperty> compoundProperties =
Map.ofEntries(
entry(
FIGHT_PROP_MAX_HP,
new CompoundProperty(
FIGHT_PROP_MAX_HP, FIGHT_PROP_BASE_HP, FIGHT_PROP_HP_PERCENT, FIGHT_PROP_HP)),
entry(
FIGHT_PROP_CUR_ATTACK,
new CompoundProperty(
FIGHT_PROP_CUR_ATTACK,
FIGHT_PROP_BASE_ATTACK,
FIGHT_PROP_ATTACK_PERCENT,
FIGHT_PROP_ATTACK)),
entry(
FIGHT_PROP_CUR_DEFENSE,
new CompoundProperty(
FIGHT_PROP_CUR_DEFENSE,
FIGHT_PROP_BASE_DEFENSE,
FIGHT_PROP_DEFENSE_PERCENT,
FIGHT_PROP_DEFENSE)));
static {
Stream.of(values())
.forEach(
e -> {
map.put(e.getId(), e);
stringMap.put(e.name(), e);
});
}
private final int id;
FightProperty(int id) {
this.id = id;
}
public static FightProperty getPropById(int value) {
return map.getOrDefault(value, FIGHT_PROP_NONE);
}
public static FightProperty getPropByName(String name) {
return stringMap.getOrDefault(name, FIGHT_PROP_NONE);
}
public static FightProperty getPropByShortName(String name) {
return shortNameMap.getOrDefault(name, FIGHT_PROP_NONE);
}
public static Set<String> getShortNames() {
return shortNameMap.keySet();
}
public static CompoundProperty getCompoundProperty(FightProperty result) {
return compoundProperties.get(result);
}
public static void forEachCompoundProperty(Consumer<CompoundProperty> consumer) {
compoundProperties.values().forEach(consumer);
}
public static boolean isPercentage(FightProperty prop) {
return !flatProps.contains(prop);
}
public int getId() {
return id;
}
@Getter
public static class CompoundProperty {
private final FightProperty result;
private final FightProperty base;
private final FightProperty percent;
private final FightProperty flat;
public CompoundProperty(
FightProperty result, FightProperty base, FightProperty percent, FightProperty flat) {
this.result = result;
this.base = base;
this.percent = percent;
this.flat = flat;
}
}
}
package emu.grasscutter.game.props;
import static java.util.Map.entry;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Stream;
import lombok.Getter;
public enum FightProperty {
FIGHT_PROP_NONE(0),
FIGHT_PROP_BASE_HP(1),
FIGHT_PROP_HP(2),
FIGHT_PROP_HP_PERCENT(3),
FIGHT_PROP_BASE_ATTACK(4),
FIGHT_PROP_ATTACK(5),
FIGHT_PROP_ATTACK_PERCENT(6),
FIGHT_PROP_BASE_DEFENSE(7),
FIGHT_PROP_DEFENSE(8),
FIGHT_PROP_DEFENSE_PERCENT(9),
FIGHT_PROP_BASE_SPEED(10),
FIGHT_PROP_SPEED_PERCENT(11),
FIGHT_PROP_HP_MP_PERCENT(12),
FIGHT_PROP_ATTACK_MP_PERCENT(13),
FIGHT_PROP_CRITICAL(20),
FIGHT_PROP_ANTI_CRITICAL(21),
FIGHT_PROP_CRITICAL_HURT(22),
FIGHT_PROP_CHARGE_EFFICIENCY(23),
FIGHT_PROP_ADD_HURT(24),
FIGHT_PROP_SUB_HURT(25),
FIGHT_PROP_HEAL_ADD(26),
FIGHT_PROP_HEALED_ADD(27),
FIGHT_PROP_ELEMENT_MASTERY(28),
FIGHT_PROP_PHYSICAL_SUB_HURT(29),
FIGHT_PROP_PHYSICAL_ADD_HURT(30),
FIGHT_PROP_DEFENCE_IGNORE_RATIO(31),
FIGHT_PROP_DEFENCE_IGNORE_DELTA(32),
FIGHT_PROP_FIRE_ADD_HURT(40),
FIGHT_PROP_ELEC_ADD_HURT(41),
FIGHT_PROP_WATER_ADD_HURT(42),
FIGHT_PROP_GRASS_ADD_HURT(43),
FIGHT_PROP_WIND_ADD_HURT(44),
FIGHT_PROP_ROCK_ADD_HURT(45),
FIGHT_PROP_ICE_ADD_HURT(46),
FIGHT_PROP_HIT_HEAD_ADD_HURT(47),
FIGHT_PROP_FIRE_SUB_HURT(50),
FIGHT_PROP_ELEC_SUB_HURT(51),
FIGHT_PROP_WATER_SUB_HURT(52),
FIGHT_PROP_GRASS_SUB_HURT(53),
FIGHT_PROP_WIND_SUB_HURT(54),
FIGHT_PROP_ROCK_SUB_HURT(55),
FIGHT_PROP_ICE_SUB_HURT(56),
FIGHT_PROP_EFFECT_HIT(60),
FIGHT_PROP_EFFECT_RESIST(61),
FIGHT_PROP_FREEZE_RESIST(62),
FIGHT_PROP_TORPOR_RESIST(63),
FIGHT_PROP_DIZZY_RESIST(64),
FIGHT_PROP_FREEZE_SHORTEN(65),
FIGHT_PROP_TORPOR_SHORTEN(66),
FIGHT_PROP_DIZZY_SHORTEN(67),
FIGHT_PROP_MAX_FIRE_ENERGY(70),
FIGHT_PROP_MAX_ELEC_ENERGY(71),
FIGHT_PROP_MAX_WATER_ENERGY(72),
FIGHT_PROP_MAX_GRASS_ENERGY(73),
FIGHT_PROP_MAX_WIND_ENERGY(74),
FIGHT_PROP_MAX_ICE_ENERGY(75),
FIGHT_PROP_MAX_ROCK_ENERGY(76),
FIGHT_PROP_SKILL_CD_MINUS_RATIO(80),
FIGHT_PROP_SHIELD_COST_MINUS_RATIO(81),
FIGHT_PROP_CUR_FIRE_ENERGY(1000),
FIGHT_PROP_CUR_ELEC_ENERGY(1001),
FIGHT_PROP_CUR_WATER_ENERGY(1002),
FIGHT_PROP_CUR_GRASS_ENERGY(1003),
FIGHT_PROP_CUR_WIND_ENERGY(1004),
FIGHT_PROP_CUR_ICE_ENERGY(1005),
FIGHT_PROP_CUR_ROCK_ENERGY(1006),
FIGHT_PROP_CUR_HP(1010),
FIGHT_PROP_MAX_HP(2000),
FIGHT_PROP_CUR_ATTACK(2001),
FIGHT_PROP_CUR_DEFENSE(2002),
FIGHT_PROP_CUR_SPEED(2003),
FIGHT_PROP_NONEXTRA_ATTACK(3000),
FIGHT_PROP_NONEXTRA_DEFENSE(3001),
FIGHT_PROP_NONEXTRA_CRITICAL(3002),
FIGHT_PROP_NONEXTRA_ANTI_CRITICAL(3003),
FIGHT_PROP_NONEXTRA_CRITICAL_HURT(3004),
FIGHT_PROP_NONEXTRA_CHARGE_EFFICIENCY(3005),
FIGHT_PROP_NONEXTRA_ELEMENT_MASTERY(3006),
FIGHT_PROP_NONEXTRA_PHYSICAL_SUB_HURT(3007),
FIGHT_PROP_NONEXTRA_FIRE_ADD_HURT(3008),
FIGHT_PROP_NONEXTRA_ELEC_ADD_HURT(3009),
FIGHT_PROP_NONEXTRA_WATER_ADD_HURT(3010),
FIGHT_PROP_NONEXTRA_GRASS_ADD_HURT(3011),
FIGHT_PROP_NONEXTRA_WIND_ADD_HURT(3012),
FIGHT_PROP_NONEXTRA_ROCK_ADD_HURT(3013),
FIGHT_PROP_NONEXTRA_ICE_ADD_HURT(3014),
FIGHT_PROP_NONEXTRA_FIRE_SUB_HURT(3015),
FIGHT_PROP_NONEXTRA_ELEC_SUB_HURT(3016),
FIGHT_PROP_NONEXTRA_WATER_SUB_HURT(3017),
FIGHT_PROP_NONEXTRA_GRASS_SUB_HURT(3018),
FIGHT_PROP_NONEXTRA_WIND_SUB_HURT(3019),
FIGHT_PROP_NONEXTRA_ROCK_SUB_HURT(3020),
FIGHT_PROP_NONEXTRA_ICE_SUB_HURT(3021),
FIGHT_PROP_NONEXTRA_SKILL_CD_MINUS_RATIO(3022),
FIGHT_PROP_NONEXTRA_SHIELD_COST_MINUS_RATIO(3023),
FIGHT_PROP_NONEXTRA_PHYSICAL_ADD_HURT(3024);
public static final int[] fightProps =
new int[] {
1, 4, 7, 20, 21, 22, 23, 26, 27, 28, 29, 30, 40, 41, 42, 43, 44, 45, 46, 50, 51, 52, 53, 54,
55, 56, 2000, 2001, 2002, 2003, 1010
};
private static final Int2ObjectMap<FightProperty> map = new Int2ObjectOpenHashMap<>();
private static final Map<String, FightProperty> stringMap = new HashMap<>();
// This was originally for relic properties so some names might not be applicable for e.g.
// setstats
private static final Map<String, FightProperty> shortNameMap =
Map.ofEntries(
// Normal relic stats
entry("hp", FIGHT_PROP_HP),
entry("atk", FIGHT_PROP_ATTACK),
entry("def", FIGHT_PROP_DEFENSE),
entry("hp%", FIGHT_PROP_HP_PERCENT),
entry("atk%", FIGHT_PROP_ATTACK_PERCENT),
entry("def%", FIGHT_PROP_DEFENSE_PERCENT),
entry("em", FIGHT_PROP_ELEMENT_MASTERY),
entry("er", FIGHT_PROP_CHARGE_EFFICIENCY),
entry("hb", FIGHT_PROP_HEAL_ADD),
entry("heal", FIGHT_PROP_HEAL_ADD),
entry("cd", FIGHT_PROP_CRITICAL_HURT),
entry("cdmg", FIGHT_PROP_CRITICAL_HURT),
entry("cr", FIGHT_PROP_CRITICAL),
entry("crate", FIGHT_PROP_CRITICAL),
entry("phys%", FIGHT_PROP_PHYSICAL_ADD_HURT),
entry("dendro%", FIGHT_PROP_GRASS_ADD_HURT),
entry("geo%", FIGHT_PROP_ROCK_ADD_HURT),
entry("anemo%", FIGHT_PROP_WIND_ADD_HURT),
entry("hydro%", FIGHT_PROP_WATER_ADD_HURT),
entry("cryo%", FIGHT_PROP_ICE_ADD_HURT),
entry("electro%", FIGHT_PROP_ELEC_ADD_HURT),
entry("pyro%", FIGHT_PROP_FIRE_ADD_HURT),
// Other stats
entry("maxhp", FIGHT_PROP_MAX_HP),
entry("dmg", FIGHT_PROP_ADD_HURT), // This seems to get reset after attacks
entry("cdr", FIGHT_PROP_SKILL_CD_MINUS_RATIO),
entry("heali", FIGHT_PROP_HEALED_ADD),
entry("shield", FIGHT_PROP_SHIELD_COST_MINUS_RATIO),
entry("defi", FIGHT_PROP_DEFENCE_IGNORE_RATIO),
entry("resall", FIGHT_PROP_SUB_HURT), // This seems to get reset after attacks
entry("resanemo", FIGHT_PROP_WIND_SUB_HURT),
entry("rescryo", FIGHT_PROP_ICE_SUB_HURT),
entry("resdendro", FIGHT_PROP_GRASS_SUB_HURT),
entry("reselectro", FIGHT_PROP_ELEC_SUB_HURT),
entry("resgeo", FIGHT_PROP_ROCK_SUB_HURT),
entry("reshydro", FIGHT_PROP_WATER_SUB_HURT),
entry("respyro", FIGHT_PROP_FIRE_SUB_HURT),
entry("resphys", FIGHT_PROP_PHYSICAL_SUB_HURT));
private static final List<FightProperty> flatProps =
Arrays.asList(
FIGHT_PROP_BASE_HP,
FIGHT_PROP_HP,
FIGHT_PROP_BASE_ATTACK,
FIGHT_PROP_ATTACK,
FIGHT_PROP_BASE_DEFENSE,
FIGHT_PROP_DEFENSE,
FIGHT_PROP_HEALED_ADD,
FIGHT_PROP_CUR_FIRE_ENERGY,
FIGHT_PROP_CUR_ELEC_ENERGY,
FIGHT_PROP_CUR_WATER_ENERGY,
FIGHT_PROP_CUR_GRASS_ENERGY,
FIGHT_PROP_CUR_WIND_ENERGY,
FIGHT_PROP_CUR_ICE_ENERGY,
FIGHT_PROP_CUR_ROCK_ENERGY,
FIGHT_PROP_CUR_HP,
FIGHT_PROP_MAX_HP,
FIGHT_PROP_CUR_ATTACK,
FIGHT_PROP_CUR_DEFENSE);
private static final Map<FightProperty, CompoundProperty> compoundProperties =
Map.ofEntries(
entry(
FIGHT_PROP_MAX_HP,
new CompoundProperty(
FIGHT_PROP_MAX_HP, FIGHT_PROP_BASE_HP, FIGHT_PROP_HP_PERCENT, FIGHT_PROP_HP)),
entry(
FIGHT_PROP_CUR_ATTACK,
new CompoundProperty(
FIGHT_PROP_CUR_ATTACK,
FIGHT_PROP_BASE_ATTACK,
FIGHT_PROP_ATTACK_PERCENT,
FIGHT_PROP_ATTACK)),
entry(
FIGHT_PROP_CUR_DEFENSE,
new CompoundProperty(
FIGHT_PROP_CUR_DEFENSE,
FIGHT_PROP_BASE_DEFENSE,
FIGHT_PROP_DEFENSE_PERCENT,
FIGHT_PROP_DEFENSE)));
static {
Stream.of(values())
.forEach(
e -> {
map.put(e.getId(), e);
stringMap.put(e.name(), e);
});
}
private final int id;
FightProperty(int id) {
this.id = id;
}
public static FightProperty getPropById(int value) {
return map.getOrDefault(value, FIGHT_PROP_NONE);
}
public static FightProperty getPropByName(String name) {
return stringMap.getOrDefault(name, FIGHT_PROP_NONE);
}
public static FightProperty getPropByShortName(String name) {
return shortNameMap.getOrDefault(name, FIGHT_PROP_NONE);
}
public static Set<String> getShortNames() {
return shortNameMap.keySet();
}
public static CompoundProperty getCompoundProperty(FightProperty result) {
return compoundProperties.get(result);
}
public static void forEachCompoundProperty(Consumer<CompoundProperty> consumer) {
compoundProperties.values().forEach(consumer);
}
public static boolean isPercentage(FightProperty prop) {
return !flatProps.contains(prop);
}
public int getId() {
return id;
}
@Getter
public static class CompoundProperty {
private final FightProperty result;
private final FightProperty base;
private final FightProperty percent;
private final FightProperty flat;
public CompoundProperty(
FightProperty result, FightProperty base, FightProperty percent, FightProperty flat) {
this.result = result;
this.base = base;
this.percent = percent;
this.flat = flat;
}
}
}

View File

@ -1,22 +1,22 @@
package emu.grasscutter.game.props.ItemUseAction;
import emu.grasscutter.game.props.ItemUseOp;
public class ItemUseAddCurHp extends ItemUseInt {
private final String icon;
public ItemUseAddCurHp(String[] useParam) {
super(useParam);
this.icon = useParam[1];
}
@Override
public ItemUseOp getItemUseOp() {
return ItemUseOp.ITEM_USE_ADD_CUR_HP;
}
@Override
public boolean useItem(UseItemParams params) {
return (params.targetAvatar.getAsEntity().heal(params.count * this.i) > 0.01);
}
}
package emu.grasscutter.game.props.ItemUseAction;
import emu.grasscutter.game.props.ItemUseOp;
public class ItemUseAddCurHp extends ItemUseInt {
private final String icon;
public ItemUseAddCurHp(String[] useParam) {
super(useParam);
this.icon = useParam[1];
}
@Override
public ItemUseOp getItemUseOp() {
return ItemUseOp.ITEM_USE_ADD_CUR_HP;
}
@Override
public boolean useItem(UseItemParams params) {
return (params.targetAvatar.getAsEntity().heal(params.count * this.i) > 0.01);
}
}

View File

@ -1,38 +1,38 @@
package emu.grasscutter.game.props.ItemUseAction;
import emu.grasscutter.game.props.ItemUseOp;
public class ItemUseChestSelectItem extends ItemUseSelectItems {
private final int[] optionItemCounts;
public ItemUseChestSelectItem(String[] useParam) {
String[] options = useParam[0].split(",");
this.optionItemIds = new int[options.length];
this.optionItemCounts = new int[options.length];
for (int i = 0; i < options.length; i++) {
String[] optionParts = options[i].split(":");
try {
this.optionItemIds[i] = Integer.parseInt(optionParts[0]);
} catch (NumberFormatException ignored) {
this.optionItemIds[i] = INVALID;
}
try {
this.optionItemCounts[i] = Integer.parseInt(optionParts[1]);
} catch (NumberFormatException ignored) {
this.optionItemCounts[i] = INVALID;
}
}
}
@Override
public ItemUseOp getItemUseOp() {
return ItemUseOp.ITEM_USE_CHEST_SELECT_ITEM;
}
@Override
protected int getItemCount(int index) {
if ((optionItemCounts == null) || (index < 0) || (index > optionItemCounts.length))
return INVALID;
return this.optionItemCounts[index];
}
}
package emu.grasscutter.game.props.ItemUseAction;
import emu.grasscutter.game.props.ItemUseOp;
public class ItemUseChestSelectItem extends ItemUseSelectItems {
private final int[] optionItemCounts;
public ItemUseChestSelectItem(String[] useParam) {
String[] options = useParam[0].split(",");
this.optionItemIds = new int[options.length];
this.optionItemCounts = new int[options.length];
for (int i = 0; i < options.length; i++) {
String[] optionParts = options[i].split(":");
try {
this.optionItemIds[i] = Integer.parseInt(optionParts[0]);
} catch (NumberFormatException ignored) {
this.optionItemIds[i] = INVALID;
}
try {
this.optionItemCounts[i] = Integer.parseInt(optionParts[1]);
} catch (NumberFormatException ignored) {
this.optionItemCounts[i] = INVALID;
}
}
}
@Override
public ItemUseOp getItemUseOp() {
return ItemUseOp.ITEM_USE_CHEST_SELECT_ITEM;
}
@Override
protected int getItemCount(int index) {
if ((optionItemCounts == null) || (index < 0) || (index > optionItemCounts.length))
return INVALID;
return this.optionItemCounts[index];
}
}

View File

@ -1,97 +1,97 @@
package emu.grasscutter.game.world;
import emu.grasscutter.data.GameDepot;
import emu.grasscutter.utils.Position;
import java.util.List;
import java.util.Objects;
import lombok.Getter;
import lombok.Setter;
public class SpawnDataEntry {
@Getter @Setter private transient SpawnGroupEntry group;
@Getter private int monsterId;
@Getter private int gadgetId;
@Getter private int configId;
@Getter private int level;
@Getter private int poseId;
@Getter private int gatherItemId;
@Getter private int gadgetState;
@Getter private Position pos;
@Getter private Position rot;
public GridBlockId getBlockId() {
int scale = GridBlockId.getScale(gadgetId);
return new GridBlockId(
group.sceneId,
scale,
(int) (pos.getX() / GameDepot.BLOCK_SIZE[scale]),
(int) (pos.getZ() / GameDepot.BLOCK_SIZE[scale]));
}
public static class SpawnGroupEntry {
@Getter private int sceneId;
@Getter private int groupId;
@Getter private int blockId;
@Getter @Setter private List<SpawnDataEntry> spawns;
}
public static class GridBlockId {
@Getter private final int sceneId;
@Getter private final int scale;
@Getter private final int x;
@Getter private final int z;
public GridBlockId(int sceneId, int scale, int x, int z) {
this.sceneId = sceneId;
this.scale = scale;
this.x = x;
this.z = z;
}
public static GridBlockId[] getAdjacentGridBlockIds(int sceneId, Position pos) {
GridBlockId[] results = new GridBlockId[5 * 5 * GameDepot.BLOCK_SIZE.length];
int t = 0;
for (int scale = 0; scale < GameDepot.BLOCK_SIZE.length; scale++) {
int x = ((int) (pos.getX() / GameDepot.BLOCK_SIZE[scale]));
int z = ((int) (pos.getZ() / GameDepot.BLOCK_SIZE[scale]));
for (int i = x - 2; i < x + 3; i++) {
for (int j = z - 2; j < z + 3; j++) {
results[t++] = new GridBlockId(sceneId, scale, i, j);
}
}
}
return results;
}
public static int getScale(int gadgetId) {
return 0; // you should implement here,this is index of GameDepot.BLOCK_SIZE
}
@Override
public String toString() {
return "SpawnDataEntryScaledPoint{"
+ "sceneId="
+ sceneId
+ ", scale="
+ scale
+ ", x="
+ x
+ ", z="
+ z
+ '}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GridBlockId that = (GridBlockId) o;
return sceneId == that.sceneId && scale == that.scale && x == that.x && z == that.z;
}
@Override
public int hashCode() {
return Objects.hash(sceneId, scale, x, z);
}
}
}
package emu.grasscutter.game.world;
import emu.grasscutter.data.GameDepot;
import emu.grasscutter.utils.Position;
import java.util.List;
import java.util.Objects;
import lombok.Getter;
import lombok.Setter;
public class SpawnDataEntry {
@Getter @Setter private transient SpawnGroupEntry group;
@Getter private int monsterId;
@Getter private int gadgetId;
@Getter private int configId;
@Getter private int level;
@Getter private int poseId;
@Getter private int gatherItemId;
@Getter private int gadgetState;
@Getter private Position pos;
@Getter private Position rot;
public GridBlockId getBlockId() {
int scale = GridBlockId.getScale(gadgetId);
return new GridBlockId(
group.sceneId,
scale,
(int) (pos.getX() / GameDepot.BLOCK_SIZE[scale]),
(int) (pos.getZ() / GameDepot.BLOCK_SIZE[scale]));
}
public static class SpawnGroupEntry {
@Getter private int sceneId;
@Getter private int groupId;
@Getter private int blockId;
@Getter @Setter private List<SpawnDataEntry> spawns;
}
public static class GridBlockId {
@Getter private int sceneId;
@Getter private int scale;
@Getter private int x;
@Getter private int z;
public GridBlockId(int sceneId, int scale, int x, int z) {
this.sceneId = sceneId;
this.scale = scale;
this.x = x;
this.z = z;
}
public static GridBlockId[] getAdjacentGridBlockIds(int sceneId, Position pos) {
GridBlockId[] results = new GridBlockId[5 * 5 * GameDepot.BLOCK_SIZE.length];
int t = 0;
for (int scale = 0; scale < GameDepot.BLOCK_SIZE.length; scale++) {
int x = ((int) (pos.getX() / GameDepot.BLOCK_SIZE[scale]));
int z = ((int) (pos.getZ() / GameDepot.BLOCK_SIZE[scale]));
for (int i = x - 2; i < x + 3; i++) {
for (int j = z - 2; j < z + 3; j++) {
results[t++] = new GridBlockId(sceneId, scale, i, j);
}
}
}
return results;
}
public static int getScale(int gadgetId) {
return 0; // you should implement here,this is index of GameDepot.BLOCK_SIZE
}
@Override
public String toString() {
return "SpawnDataEntryScaledPoint{"
+ "sceneId="
+ sceneId
+ ", scale="
+ scale
+ ", x="
+ x
+ ", z="
+ z
+ '}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GridBlockId that = (GridBlockId) o;
return sceneId == that.sceneId && scale == that.scale && x == that.x && z == that.z;
}
@Override
public int hashCode() {
return Objects.hash(sceneId, scale, x, z);
}
}
}

View File

@ -37,12 +37,12 @@ public class World implements Iterable<Player> {
@Getter private final List<Player> players;
@Getter private final Int2ObjectMap<Scene> scenes;
@Getter private final int levelEntityId;
@Getter private int levelEntityId;
private int nextEntityId = 0;
private int nextPeerId = 0;
private int worldLevel;
private final boolean isMultiplayer;
private boolean isMultiplayer;
private long lastUpdateTime;
@Getter private int tickCount = 0;

View File

@ -36,10 +36,10 @@ public class ScriptLoader {
@Getter private static ScriptLib scriptLib;
@Getter private static LuaValue scriptLibLua;
/** suggest GC to remove it if the memory is less */
private static final Map<String, SoftReference<CompiledScript>> scriptsCache =
private static Map<String, SoftReference<CompiledScript>> scriptsCache =
new ConcurrentHashMap<>();
/** sceneId - SceneMeta */
private static final Map<Integer, SoftReference<SceneMeta>> sceneMetaCache =
private static Map<Integer, SoftReference<SceneMeta>> sceneMetaCache =
new ConcurrentHashMap<>();
public static synchronized void init() throws Exception {

View File

@ -1,26 +1,26 @@
package emu.grasscutter.server.http.documentation;
import static emu.grasscutter.config.Configuration.DOCUMENT_LANGUAGE;
import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.Language;
import io.javalin.http.ContentType;
import io.javalin.http.Context;
import java.util.List;
final class GachaMappingRequestHandler implements DocumentationHandler {
private final List<String> gachaJsons;
GachaMappingRequestHandler() {
this.gachaJsons = Tools.createGachaMappingJsons();
}
@Override
public void handle(Context ctx) {
final int langIdx =
Language.TextStrings.MAP_LANGUAGES.getOrDefault(
DOCUMENT_LANGUAGE,
0); // TODO: This should really be based off the client language somehow
ctx.contentType(ContentType.APPLICATION_JSON).result(gachaJsons.get(langIdx));
}
}
package emu.grasscutter.server.http.documentation;
import static emu.grasscutter.config.Configuration.DOCUMENT_LANGUAGE;
import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.Language;
import io.javalin.http.ContentType;
import io.javalin.http.Context;
import java.util.List;
final class GachaMappingRequestHandler implements DocumentationHandler {
private final List<String> gachaJsons;
GachaMappingRequestHandler() {
this.gachaJsons = Tools.createGachaMappingJsons();
}
@Override
public void handle(Context ctx) {
final int langIdx =
Language.TextStrings.MAP_LANGUAGES.getOrDefault(
DOCUMENT_LANGUAGE,
0); // TODO: This should really be based off the client language somehow
ctx.contentType(ContentType.APPLICATION_JSON).result(gachaJsons.get(langIdx));
}
}

View File

@ -1,48 +1,48 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo;
import emu.grasscutter.net.proto.PrivateChatNotifyOuterClass.PrivateChatNotify;
public class PacketPrivateChatNotify extends BasePacket {
private final ChatInfo info;
public PacketPrivateChatNotify(int senderId, int recvId, String message) {
super(PacketOpcodes.PrivateChatNotify);
ChatInfo info =
ChatInfo.newBuilder()
.setTime((int) (System.currentTimeMillis() / 1000))
.setUid(senderId)
.setToUid(recvId)
.setText(message)
.build();
this.info = info;
PrivateChatNotify proto = PrivateChatNotify.newBuilder().setChatInfo(info).build();
this.setData(proto);
}
public PacketPrivateChatNotify(int senderId, int recvId, int emote) {
super(PacketOpcodes.PrivateChatNotify);
ChatInfo info =
ChatInfo.newBuilder()
.setTime((int) (System.currentTimeMillis() / 1000))
.setUid(senderId)
.setToUid(recvId)
.setIcon(emote)
.build();
this.info = info;
PrivateChatNotify proto = PrivateChatNotify.newBuilder().setChatInfo(info).build();
this.setData(proto);
}
public ChatInfo getChatInfo() {
return this.info;
}
}
package emu.grasscutter.server.packet.send;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo;
import emu.grasscutter.net.proto.PrivateChatNotifyOuterClass.PrivateChatNotify;
public class PacketPrivateChatNotify extends BasePacket {
private final ChatInfo info;
public PacketPrivateChatNotify(int senderId, int recvId, String message) {
super(PacketOpcodes.PrivateChatNotify);
ChatInfo info =
ChatInfo.newBuilder()
.setTime((int) (System.currentTimeMillis() / 1000))
.setUid(senderId)
.setToUid(recvId)
.setText(message)
.build();
this.info = info;
PrivateChatNotify proto = PrivateChatNotify.newBuilder().setChatInfo(info).build();
this.setData(proto);
}
public PacketPrivateChatNotify(int senderId, int recvId, int emote) {
super(PacketOpcodes.PrivateChatNotify);
ChatInfo info =
ChatInfo.newBuilder()
.setTime((int) (System.currentTimeMillis() / 1000))
.setUid(senderId)
.setToUid(recvId)
.setIcon(emote)
.build();
this.info = info;
PrivateChatNotify proto = PrivateChatNotify.newBuilder().setChatInfo(info).build();
this.setData(proto);
}
public ChatInfo getChatInfo() {
return this.info;
}
}

View File

@ -57,7 +57,7 @@ public class TsvUtils {
private static final Function<String, Object> parseLong =
value -> (long) Double.parseDouble(value); // Long::parseLong;
private static final Map<Class<?>, Function<String, Object>> enumTypeParsers = new HashMap<>();
private static final Map<Type, Function<String, Object>> primitiveTypeParsers =
private final static Map<Type, Function<String, Object>> primitiveTypeParsers =
Map.ofEntries(
Map.entry(String.class, parseString),
Map.entry(Integer.class, parseInt),
@ -72,7 +72,7 @@ public class TsvUtils {
Map.entry(boolean.class, Boolean::parseBoolean));
private static final Map<Type, Function<String, Object>> typeParsers =
new HashMap<>(primitiveTypeParsers);
private static final Map<Class<?>, Map<String, FieldParser>> cachedClassFieldMaps =
private final static Map<Class<?>, Map<String, FieldParser>> cachedClassFieldMaps =
new HashMap<>();
@SuppressWarnings("unchecked")