[BREAKING] Item Usage Overhaul

-De-hardcode elemental orb values
-De-hardcode exp items
-Change ShopChest format (temporary, drop system overhaul will replace it entirely)
-Food healing actually uses Ability data for real HP amounts
This commit is contained in:
AnimeGitB
2022-10-14 00:00:40 +10:30
parent 5bb43ac074
commit d1d39db56c
66 changed files with 1533 additions and 786 deletions

View File

@@ -25,10 +25,12 @@ public class GameData {
// BinOutputs
@Getter private static final Int2ObjectMap<HomeworldDefaultSaveData> homeworldDefaultSaveData = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<String> abilityHashes = new Int2ObjectOpenHashMap<>();
@Deprecated(forRemoval = true)
@Getter private static final Map<String, AbilityModifierEntry> abilityModifiers = new HashMap<>();
@Getter private static final Map<String, ConfigGadget> gadgetConfigData = new HashMap<>();
@Getter private static final Map<String, OpenConfigEntry> openConfigEntries = new HashMap<>();
@Deprecated(forRemoval = true) @Getter private static final Map<String, ScenePointEntry> scenePointEntries = new HashMap<>();
protected static final Map<String, AbilityData> abilityDataMap = new HashMap<>();
protected static final Int2ObjectMap<ScenePointEntry> scenePointEntryMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<MainQuestData> mainQuestData = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<QuestEncryptionKey> questsKeys = new Int2ObjectOpenHashMap<>();
@@ -136,6 +138,7 @@ public class GameData {
public static Map<String, AbilityEmbryoEntry> getAbilityEmbryoInfo() {return abilityEmbryos;}
// Getters that get values rather than containers. If Lombok ever gets syntactic sugar for this, we should adopt that.
public static AbilityData getAbilityData(String abilityName) {return abilityDataMap.get(abilityName);}
public static IntSet getAvatarSkillLevels(int avatarSkillId) {return avatarSkillLevels.get(avatarSkillId);}
public static IntSet getProudSkillGroupLevels(int proudSkillGroupId) {return proudSkillGroupLevels.get(proudSkillGroupId);}

View File

@@ -3,8 +3,7 @@ package emu.grasscutter.data;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.binout.*;
import emu.grasscutter.data.binout.AbilityModifier.AbilityConfigData;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierActionType;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
import emu.grasscutter.data.common.PointData;
import emu.grasscutter.game.managers.blossom.BlossomConfig;
import emu.grasscutter.game.quest.QuestEncryptionKey;
@@ -22,6 +21,7 @@ import org.reflections.Reflections;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Stream;
@@ -237,57 +237,54 @@ public class ResourceLoader {
}
}
// private static HashSet<String> modifierActionTypes = new HashSet<>();
public static class AbilityConfigData {
public AbilityData Default;
}
private static void loadAbilityModifiers() {
// Load from BinOutput
try {
Files.newDirectoryStream(getResourcePath("BinOutput/Ability/Temp/AvatarAbilities/")).forEach(path -> {
List<AbilityConfigData> abilityConfigList;
try {
abilityConfigList = JsonUtils.loadToList(path, AbilityConfigData.class);
} catch (IOException e) {
Grasscutter.getLogger().error("Error loading ability modifiers from path " + path.toString() + ": ", e);
return;
}
abilityConfigList.forEach(data -> {
if (data.Default.modifiers == null || data.Default.modifiers.size() == 0) {
return;
}
String name = data.Default.abilityName;
AbilityModifierEntry modifierEntry = new AbilityModifierEntry(name);
data.Default.modifiers.forEach((key, modifier) -> {
Stream.ofNullable(modifier.onAdded)
.flatMap(Stream::of)
.filter(action -> action.$type.contains("HealHP"))
.forEach(action -> {
action.type = AbilityModifierActionType.HealHP;
modifierEntry.getOnAdded().add(action);
});
Stream.ofNullable(modifier.onThinkInterval)
.flatMap(Stream::of)
.filter(action -> action.$type.contains("HealHP"))
.forEach(action -> {
action.type = AbilityModifierActionType.HealHP;
modifierEntry.getOnThinkInterval().add(action);
});
Stream.ofNullable(modifier.onRemoved)
.flatMap(Stream::of)
.filter(action -> action.$type.contains("HealHP"))
.forEach(action -> {
action.type = AbilityModifierActionType.HealHP;
modifierEntry.getOnRemoved().add(action);
});
});
GameData.getAbilityModifiers().put(name, modifierEntry);
});
});
try (Stream<Path> paths = Files.walk(getResourcePath("BinOutput/Ability/Temp/"))) {
paths.filter(Files::isRegularFile).filter(path -> path.toString().endsWith(".json")).forEach(ResourceLoader::loadAbilityModifiers);
} catch (IOException e) {
Grasscutter.getLogger().error("Error loading ability modifiers: ", e);
return;
}
// System.out.println("Loaded modifiers, found types:");
// modifierActionTypes.stream().sorted().forEach(s -> System.out.printf("%s, ", s));
// System.out.println("[End]");
}
private static void loadAbilityModifiers(Path path) {
try {
JsonUtils.loadToList(path, AbilityConfigData.class).forEach(data -> loadAbilityData(data.Default));
} catch (IOException e) {
Grasscutter.getLogger().error("Error loading ability modifiers from path " + path.toString() + ": ", e);
return;
}
}
private static void loadAbilityData(AbilityData data) {
GameData.abilityDataMap.put(data.abilityName, data);
val modifiers = data.modifiers;
if (modifiers == null || modifiers.size() == 0) return;
String name = data.abilityName;
AbilityModifierEntry modifierEntry = new AbilityModifierEntry(name);
modifiers.forEach((key, modifier) -> {
Stream.ofNullable(modifier.onAdded).flatMap(Stream::of)
// .map(action -> {modifierActionTypes.add(action.$type); return action;})
.filter(action -> action.type == AbilityModifierAction.Type.HealHP)
.forEach(action -> modifierEntry.getOnAdded().add(action));
Stream.ofNullable(modifier.onThinkInterval).flatMap(Stream::of)
// .map(action -> {modifierActionTypes.add(action.$type); return action;})
.filter(action -> action.type == AbilityModifierAction.Type.HealHP)
.forEach(action -> modifierEntry.getOnThinkInterval().add(action));
Stream.ofNullable(modifier.onRemoved).flatMap(Stream::of)
// .map(action -> {modifierActionTypes.add(action.$type); return action;})
.filter(action -> action.type == AbilityModifierAction.Type.HealHP)
.forEach(action -> modifierEntry.getOnRemoved().add(action));
});
GameData.getAbilityModifiers().put(name, modifierEntry);
}
private static void loadSpawnData() {

View File

@@ -0,0 +1,13 @@
package emu.grasscutter.data.binout;
import java.util.Map;
public class AbilityData {
public String abilityName;
public Map<String, AbilityModifier> modifiers;
public boolean isDynamicAbility;
public Map<String, Float> abilitySpecials;
// abilityMixins
// onAbilityStart
// onKill
}

View File

@@ -1,44 +1,98 @@
package emu.grasscutter.data.binout;
import java.util.Map;
import java.io.Serializable;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.common.DynamicFloat;
public class AbilityModifier implements Serializable {
private static final long serialVersionUID = -2001232313615923575L;
private static final long serialVersionUID = -2001232313615923575L;
@SerializedName(value="onAdded", alternate={"KCICDEJLIJD"})
public AbilityModifierAction[] onAdded;
@SerializedName(value="onThinkInterval", alternate={"PBDDACFFPOE"})
public AbilityModifierAction[] onThinkInterval;
public AbilityModifierAction[] onRemoved;
@SerializedName(value="onAdded", alternate={"KCICDEJLIJD"})
public AbilityModifierAction[] onAdded;
@SerializedName(value="onThinkInterval", alternate={"PBDDACFFPOE"})
public AbilityModifierAction[] onThinkInterval;
public AbilityModifierAction[] onRemoved;
public DynamicFloat duration = DynamicFloat.ZERO;
public static class AbilityConfigData {
public AbilityData Default;
}
public static class AbilityData {
public String abilityName;
@SerializedName(value="modifiers", alternate={"HNEIEGHMLKH"})
public Map<String, AbilityModifier> modifiers;
}
public static class AbilityModifierAction {
public String $type;
public AbilityModifierActionType type;
public String target;
public AbilityModifierValue amount;
public AbilityModifierValue amountByTargetCurrentHPRatio;
}
public static class AbilityModifierValue {
public boolean isFormula;
public boolean isDynamic;
public String dynamicKey;
}
public enum AbilityModifierActionType {
HealHP, ApplyModifier, LoseHP;
}
public static class AbilityModifierAction {
public enum Type {
ActCameraRadialBlur, ActCameraShake, AddAvatarSkillInfo, AddChargeBarValue,
AddClimateMeter, AddElementDurability, AddGlobalValue, AddGlobalValueToTarget,
AddRegionalPlayVarValue, ApplyModifier, AttachAbilityStateResistance, AttachBulletAimPoint,
AttachEffect, AttachEffectFirework, AttachElementTypeResistance, AttachModifier,
AttachUIEffect, AvatarCameraParam, AvatarEnterCameraShot, AvatarEnterFocus,
AvatarEnterViewBias, AvatarExitCameraShot, AvatarExitClimb, AvatarExitFocus,
AvatarExitViewBias, AvatarShareCDSkillStart, AvatarSkillStart, BroadcastNeuronStimulate,
CalcDvalinS04RebornPoint, CallLuaTask, ChangeEnviroWeather, ChangeFollowDampTime,
ChangeGadgetUIInteractHint, ChangePlayMode, ChangeTag, ChangeUGCRayTag,
ClearEndura, ClearGlobalPos, ClearGlobalValue, ClearLocalGadgets,
ClearLockTarget, ClearPos, ConfigAbilityAction, ControlEmotion,
CopyGlobalValue, CreateGadget, CreateMovingPlatform, CreateTile,
DamageByAttackValue, DebugLog, DestroyTile, DoBlink,
DoTileAction, DoWatcherSystemAction, DoWidgetSystemAction, DropSubfield,
DummyAction, DungeonFogEffects, ElementAttachForActivityGacha, EnableAIStealthy,
EnableAfterImage, EnableAvatarFlyStateTrail, EnableAvatarMoveOnWater, EnableBulletCollisionPluginTrigger,
EnableGadgetIntee, EnableHeadControl, EnableHitBoxByName, EnableMainInterface,
EnablePartControl, EnablePositionSynchronization, EnablePushColliderName, EnableRocketJump,
EnableSceneTransformByName, EnterCameraLock, EntityDoSkill, EquipAffixStart,
ExecuteGadgetLua, FireAISoundEvent, FireChargeBarEffect, FireEffect,
FireEffectFirework, FireEffectForStorm, FireFishingEvent, FireHitEffect,
FireSubEmitterEffect, FireUIEffect, FixedMonsterRushMove, ForceAirStateFly,
ForceEnableShakeOffButton, GenerateElemBall, GetFightProperty, GetInteractIdToGlobalValue,
GetPos, HealHP, HideUIBillBoard, IgnoreMoveColToRockCol,
KillGadget, KillPlayEntity, KillSelf, KillServerGadget,
LoseHP, ModifyAvatarSkillCD, ModifyVehicleSkillCD, PlayEmoSync,
Predicated, PushDvalinS01Process, PushInterActionByConfigPath, PushPos,
Randomed, ReTriggerAISkillInitialCD, RefreshUICombatBarLayout, RegisterAIActionPoint,
ReleaseAIActionPoint, RemoveAvatarSkillInfo, RemoveModifier, RemoveModifierByAbilityStateResistanceID,
RemoveServerBuff, RemoveUniqueModifier, RemoveVelocityForce, Repeated,
ResetAIAttackTarget, ResetAIResistTauntLevel, ResetAIThreatBroadcastRange, ResetAnimatorTrigger,
ReviveDeadAvatar, ReviveElemEnergy, ReviveStamina, SectorCityManeuver,
SendEffectTrigger, SendEffectTriggerToLineEffect, SendEvtElectricCoreMoveEnterP1, SendEvtElectricCoreMoveInterrupt,
ServerLuaCall, ServerLuaTriggerEvent, ServerMonsterLog, SetAIHitFeeling,
SetAISkillCDAvailableNow, SetAISkillCDMultiplier, SetAISkillGCD, SetAnimatorBool,
SetAnimatorFloat, SetAnimatorInt, SetAnimatorTrigger, SetAvatarCanShakeOff,
SetAvatarHitBuckets, SetCanDieImmediately, SetChargeBarValue, SetDvalinS01FlyState,
SetEmissionScaler, SetEntityScale, SetExtraAbilityEnable, SetExtraAbilityState,
SetGlobalDir, SetGlobalPos, SetGlobalValue, SetGlobalValueByTargetDistance,
SetGlobalValueToOverrideMap, SetKeepInAirVelocityForce, SetMaterialParamFloatByTransform, SetNeuronEnable,
SetOverrideMapValue, SetPartControlTarget, SetPoseBool, SetPoseFloat,
SetPoseInt, SetRandomOverrideMapValue, SetRegionalPlayVarValue, SetSelfAttackTarget,
SetSkillAnchor, SetSpecialCamera, SetSurroundAnchor, SetSystemValueToOverrideMap,
SetTargetNumToGlobalValue, SetUICombatBarAsh, SetUICombatBarSpark, SetVelocityIgnoreAirGY,
SetWeaponAttachPointRealName, SetWeaponBindState, ShowExtraAbility, ShowProgressBarAction,
ShowReminder, ShowScreenEffect, ShowTextMap, ShowUICombatBar,
StartDither, SumTargetWeightToSelfGlobalValue, Summon, SyncToStageScript,
TriggerAbility, TriggerAttackEvent, TriggerAttackTargetMapEvent, TriggerAudio,
TriggerAuxWeaponTrans, TriggerBullet, TriggerCreateGadgetToEquipPart, TriggerDropEquipParts,
TriggerFaceAnimation, TriggerGadgetInteractive, TriggerHideWeapon, TriggerSetCastShadow,
TriggerSetPassThrough, TriggerSetRenderersEnable, TriggerSetShadowRamp, TriggerSetVisible,
TriggerTaunt, TriggerThrowEquipPart, TriggerUGCGadgetMove, TryFindBlinkPoint,
TryFindBlinkPointByBorn, TryTriggerPlatformStartMove, TurnDirection, TurnDirectionToPos,
UpdateReactionDamage, UseSkillEliteSet, WidgetSkillStart;
}
@SerializedName("$type")
public Type type;
public String target;
@SerializedName(value = "amount", alternate = "PDLLIFICICJ")
public DynamicFloat amount = DynamicFloat.ZERO;
public DynamicFloat amountByCasterAttackRatio = DynamicFloat.ZERO;
public DynamicFloat amountByCasterCurrentHPRatio = DynamicFloat.ZERO;
public DynamicFloat amountByCasterMaxHPRatio = DynamicFloat.ZERO;
public DynamicFloat amountByGetDamage = DynamicFloat.ZERO;
public DynamicFloat amountByTargetCurrentHPRatio = DynamicFloat.ZERO;
public DynamicFloat amountByTargetMaxHPRatio = DynamicFloat.ZERO;
@SerializedName(value = "ignoreAbilityProperty", alternate = "HHFGADCJJDI")
public boolean ignoreAbilityProperty;
public String modifierName;
}
//The following should be implemented into DynamicFloat if older resource formats need to be supported
// public static class AbilityModifierValue {
// public boolean isFormula;
// public boolean isDynamic;
// public String dynamicKey;
// }
}

View File

@@ -8,6 +8,8 @@ import it.unimi.dsi.fastutil.objects.Object2FloatMap;
import lombok.val;
public class DynamicFloat {
public static DynamicFloat ZERO = new DynamicFloat(0f);
public static class StackOp {
enum Op {CONSTANT, KEY, ADD, SUB, MUL, DIV};
public Op op;

View File

@@ -5,7 +5,6 @@ import com.google.gson.annotations.SerializedName;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.DailyDungeonData;
import emu.grasscutter.utils.JsonUtils;
import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;

View File

@@ -13,6 +13,8 @@ public class BuffData extends GameResource {
private float time;
private boolean isPersistent;
private ServerBuffType serverBuffType;
private String abilityName;
private String modifierName;
@Override
public int getId() {

View File

@@ -9,7 +9,9 @@ import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.ItemUseData;
import emu.grasscutter.game.inventory.*;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.ItemUseOp;
import emu.grasscutter.game.props.ItemUseTarget;
import emu.grasscutter.game.props.ItemUseAction.ItemUseAction;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import lombok.Getter;
@@ -46,8 +48,10 @@ public class ItemData extends GameResource {
private int[] satiationParams;
// Usable item
private ItemUseTarget useTarget;
private ItemUseTarget useTarget = ItemUseTarget.ITEM_USE_TARGET_NONE;
private List<ItemUseData> itemUse;
private List<ItemUseAction> itemUseActions;
private boolean useOnGain = false;
// Relic
private int mainPropDepotId;
@@ -124,8 +128,16 @@ public class ItemData extends GameResource {
// Prevent material type from being null
this.materialType = this.materialType == null ? MaterialType.MATERIAL_NONE : this.materialType;
if (this.itemUse != null && !this.itemUse.isEmpty()) {
this.itemUseActions = this.itemUse.stream()
.filter(x -> x.getUseOp() != ItemUseOp.ITEM_USE_NONE)
.map(ItemUseAction::fromItemUseData)
.toList();
}
}
@Getter
public static class WeaponProperty {
private FightProperty propType;