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
29 changed files with 1702 additions and 1702 deletions

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;