mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-01-24 21:09:31 +08:00
[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:
parent
5bb43ac074
commit
d1d39db56c
@ -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);}
|
||||
|
||||
|
@ -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() {
|
||||
|
13
src/main/java/emu/grasscutter/data/binout/AbilityData.java
Normal file
13
src/main/java/emu/grasscutter/data/binout/AbilityData.java
Normal 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
|
||||
}
|
@ -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 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
//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;
|
||||
// }
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -195,23 +195,23 @@ public final class AbilityManager extends BasePlayerManager {
|
||||
|
||||
private void invokeAction(AbilityModifierAction action, GameEntity target, GameEntity sourceEntity) {
|
||||
switch (action.type) {
|
||||
case HealHP -> {
|
||||
}
|
||||
case HealHP -> {}
|
||||
case LoseHP -> {
|
||||
if (action.amountByTargetCurrentHPRatio == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
float damageAmount = 0;
|
||||
float damageAmount = action.amount.get();
|
||||
|
||||
if (action.amount.isDynamic && action.amount.dynamicKey != null) {
|
||||
damageAmount = sourceEntity.getMetaOverrideMap().getOrDefault(action.amount.dynamicKey, 0f);
|
||||
}
|
||||
// if (action.amount.isDynamic && action.amount.dynamicKey != null) {
|
||||
// damageAmount = sourceEntity.getMetaOverrideMap().getOrDefault(action.amount.dynamicKey, 0f);
|
||||
// }
|
||||
|
||||
if (damageAmount > 0) {
|
||||
target.damage(damageAmount);
|
||||
}
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -206,6 +206,12 @@ public class Avatar {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean addSatiation(float value) {
|
||||
if (this.satiation >= 100) return false;
|
||||
this.satiation += value;
|
||||
return true;
|
||||
}
|
||||
|
||||
public GameItem getEquipBySlot(EquipType slot) {
|
||||
return this.getEquips().get(slot.getValue());
|
||||
}
|
||||
@ -927,7 +933,7 @@ public class Avatar {
|
||||
showAvatarInfo.putPropMap(PlayerProperty.PROP_EXP.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_EXP, this.getExp()));
|
||||
showAvatarInfo.putPropMap(PlayerProperty.PROP_BREAK_LEVEL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_BREAK_LEVEL, this.getPromoteLevel()));
|
||||
showAvatarInfo.putPropMap(PlayerProperty.PROP_SATIATION_VAL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_SATIATION_VAL, this.getSatiation()));
|
||||
showAvatarInfo.putPropMap(PlayerProperty.PROP_SATIATION_PENALTY_TIME.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_SATIATION_VAL, this.getSatiationPenalty()));
|
||||
showAvatarInfo.putPropMap(PlayerProperty.PROP_SATIATION_PENALTY_TIME.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_SATIATION_PENALTY_TIME, this.getSatiationPenalty()));
|
||||
int maxStamina = this.getPlayer().getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
||||
showAvatarInfo.putPropMap(PlayerProperty.PROP_MAX_STAMINA.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_MAX_STAMINA, maxStamina));
|
||||
|
||||
|
@ -8,7 +8,6 @@ import emu.grasscutter.data.excels.CombineData;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
import emu.grasscutter.net.proto.RetcodeOuterClass;
|
||||
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||
import emu.grasscutter.server.game.BaseGameSystem;
|
||||
@ -43,24 +42,12 @@ public class CombineManger extends BaseGameSystem {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean unlockCombineDiagram(Player player, GameItem diagramItem) {
|
||||
// Make sure this is actually a diagram.
|
||||
if (diagramItem.getItemData().getItemUse().get(0).getUseOp() != ItemUseOp.ITEM_USE_UNLOCK_COMBINE) {
|
||||
return false;
|
||||
public boolean unlockCombineDiagram(Player player, int combineId) {
|
||||
if (!player.getUnlockedCombines().add(combineId)) {
|
||||
return false; // Already unlocked
|
||||
}
|
||||
|
||||
// Determine the combine item we should unlock.
|
||||
int combineId = Integer.parseInt(diagramItem.getItemData().getItemUse().get(0).getUseParam()[0]);
|
||||
|
||||
// Remove the diagram from the player's inventory.
|
||||
// We need to do this here, before sending CombineFormulaDataNotify, or the the combine UI won't correctly
|
||||
// update when unlocking the diagram.
|
||||
player.getInventory().removeItem(diagramItem, 1);
|
||||
|
||||
// Tell the client that this diagram is now unlocked and add the unlocked item to the player.
|
||||
player.getUnlockedCombines().add(combineId);
|
||||
player.sendPacket(new PacketCombineFormulaDataNotify(combineId));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -15,14 +15,13 @@ import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.excels.ItemData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.gacha.GachaBanner.BannerType;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.inventory.Inventory;
|
||||
import emu.grasscutter.game.inventory.ItemType;
|
||||
import emu.grasscutter.game.inventory.MaterialType;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
import emu.grasscutter.game.systems.InventorySystem;
|
||||
import emu.grasscutter.net.proto.GachaItemOuterClass.GachaItem;
|
||||
import emu.grasscutter.net.proto.GachaTransferItemOuterClass.GachaTransferItem;
|
||||
import emu.grasscutter.net.proto.GetGachaInfoRspOuterClass.GetGachaInfoRsp;
|
||||
@ -32,7 +31,6 @@ import emu.grasscutter.server.game.BaseGameSystem;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
import emu.grasscutter.server.game.GameServerTickEvent;
|
||||
import emu.grasscutter.server.packet.send.PacketDoGachaRsp;
|
||||
import emu.grasscutter.server.packet.send.PacketGachaWishRsp;
|
||||
import emu.grasscutter.utils.FileUtils;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
@ -121,26 +119,10 @@ public class GachaSystem extends BaseGameSystem {
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized int checkPlayerAvatarConstellationLevel(Player player, int itemId) { // Maybe this would be useful in the Player class?
|
||||
ItemData itemData = GameData.getItemDataMap().get(itemId);
|
||||
if ((itemData == null) || (itemData.getMaterialType() != MaterialType.MATERIAL_AVATAR)) {
|
||||
return -2; // Not an Avatar
|
||||
}
|
||||
Avatar avatar = player.getAvatars().getAvatarById((itemId % 1000) + 10000000);
|
||||
if (avatar == null) {
|
||||
return -1; // Doesn't have
|
||||
}
|
||||
// Constellation
|
||||
int constLevel = avatar.getCoreProudSkillLevel();
|
||||
GameItem constItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId + 100);
|
||||
constLevel += (constItem == null)? 0 : constItem.getCount();
|
||||
return constLevel;
|
||||
}
|
||||
|
||||
private synchronized int[] removeC6FromPool(int[] itemPool, Player player) {
|
||||
IntList temp = new IntArrayList();
|
||||
for (int itemId : itemPool) {
|
||||
if (checkPlayerAvatarConstellationLevel(player, itemId) < 6) {
|
||||
if (InventorySystem.checkPlayerAvatarConstellationLevel(player, itemId) < 6) {
|
||||
temp.add(itemId);
|
||||
}
|
||||
}
|
||||
@ -314,7 +296,7 @@ public class GachaSystem extends BaseGameSystem {
|
||||
boolean isTransferItem = false;
|
||||
|
||||
// Const check
|
||||
int constellation = checkPlayerAvatarConstellationLevel(player, itemId);
|
||||
int constellation = InventorySystem.checkPlayerAvatarConstellationLevel(player, itemId);
|
||||
switch (constellation) {
|
||||
case -2: // Is weapon
|
||||
switch (itemData.getRankLevel()) {
|
||||
|
@ -4,6 +4,7 @@ import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import dev.morphia.annotations.IndexOptions;
|
||||
import dev.morphia.annotations.Indexed;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.HomeWorldLevelData;
|
||||
@ -25,12 +26,12 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@Builder(builderMethodName = "of")
|
||||
public class GameHome {
|
||||
|
||||
@Id
|
||||
String id;
|
||||
|
||||
@Indexed(options = @IndexOptions(unique = true))
|
||||
long ownerUid;
|
||||
@Transient Player player;
|
||||
|
||||
int level;
|
||||
int exp;
|
||||
@ -70,6 +71,8 @@ public class GameHome {
|
||||
}
|
||||
|
||||
public void onOwnerLogin(Player player) {
|
||||
if (this.player == null)
|
||||
this.player = player;
|
||||
player.getSession().send(new PacketHomeBasicInfoNotify(player, false));
|
||||
player.getSession().send(new PacketPlayerHomeCompInfoNotify(player));
|
||||
player.getSession().send(new PacketHomeComfortInfoNotify(player));
|
||||
@ -78,30 +81,40 @@ public class GameHome {
|
||||
player.getSession().send(new PacketUnlockedHomeBgmNotify(player));
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
if (this.player == null)
|
||||
this.player = Grasscutter.getGameServer().getPlayerByUid((int) this.ownerUid, true);
|
||||
return this.player;
|
||||
}
|
||||
|
||||
public HomeWorldLevelData getLevelData(){
|
||||
return GameData.getHomeWorldLevelDataMap().get(level);
|
||||
}
|
||||
|
||||
public void addUnlockedHomeBgm(int homeBgmId) {
|
||||
getUnlockedHomeBgmList().add(homeBgmId);
|
||||
public boolean addUnlockedHomeBgm(int homeBgmId) {
|
||||
if (getUnlockedHomeBgmList().add(homeBgmId)) return false;
|
||||
|
||||
var player = this.getPlayer();
|
||||
player.sendPacket(new PacketUnlockHomeBgmNotify(homeBgmId));
|
||||
player.sendPacket(new PacketUnlockedHomeBgmNotify(player));
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
|
||||
public Set<Integer> getUnlockedHomeBgmListInfo() {
|
||||
var list = getUnlockedHomeBgmList();
|
||||
if (list == null) {
|
||||
list = new HashSet<>();
|
||||
addAllDefaultUnlockedBgmIds(list);
|
||||
setUnlockedHomeBgmList(list);
|
||||
if (this.unlockedHomeBgmList == null) {
|
||||
this.unlockedHomeBgmList = new HashSet<>();
|
||||
addAllDefaultUnlockedBgmIds(this.unlockedHomeBgmList);
|
||||
save();
|
||||
}
|
||||
|
||||
return list;
|
||||
return this.unlockedHomeBgmList;
|
||||
}
|
||||
|
||||
private void addAllDefaultUnlockedBgmIds(Set<Integer> list) {
|
||||
GameData.getHomeWorldBgmDataMap().int2ObjectEntrySet().stream()
|
||||
.filter(entry -> entry.getValue().isDefaultUnlock())
|
||||
.forEach(entry -> list.add(entry.getIntKey()));
|
||||
GameData.getHomeWorldBgmDataMap().forEach((id, data) -> {
|
||||
if (data.isDefaultUnlock())
|
||||
list.add(id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import dev.morphia.annotations.Transient;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.GameDepot;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.excels.ItemData;
|
||||
import emu.grasscutter.data.excels.ReliquaryAffixData;
|
||||
import emu.grasscutter.data.excels.ReliquaryMainPropData;
|
||||
@ -76,6 +77,10 @@ public class GameItem {
|
||||
this(GameData.getItemDataMap().get(itemId), count);
|
||||
}
|
||||
|
||||
public GameItem(ItemParamData itemParamData) {
|
||||
this(itemParamData.getId(), itemParamData.getCount());
|
||||
}
|
||||
|
||||
public GameItem(ItemData data) {
|
||||
this(data, 1);
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.props.PlayerProperty;
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
import emu.grasscutter.game.props.ItemUseAction.UseItemParams;
|
||||
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarEquipChangeNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketItemAddHintNotify;
|
||||
@ -174,7 +175,12 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
||||
|
||||
private synchronized GameItem putItem(GameItem item) {
|
||||
// Dont add items that dont have a valid item definition.
|
||||
if (item.getItemData() == null) {
|
||||
var data = item.getItemData();
|
||||
if (data == null) return null;
|
||||
|
||||
if (data.isUseOnGain()) {
|
||||
var params = new UseItemParams(this.player, data.getUseTarget());
|
||||
this.player.getServer().getInventorySystem().useItemDirect(data, params);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -202,9 +208,6 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
||||
return item;
|
||||
default:
|
||||
switch (item.getItemData().getMaterialType()) {
|
||||
case MATERIAL_ADSORBATE:
|
||||
this.player.getEnergyManager().handlePickupElemBall(item);
|
||||
return null;
|
||||
case MATERIAL_AVATAR:
|
||||
// Get avatar id
|
||||
int avatarId = (item.getItemId() % 1000) + 10000000;
|
||||
|
@ -1,7 +1,6 @@
|
||||
package emu.grasscutter.game.managers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Dictionary;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -46,22 +45,13 @@ public class CookingManager extends BasePlayerManager {
|
||||
/********************
|
||||
* Unlocking for recipies.
|
||||
********************/
|
||||
public synchronized boolean unlockRecipe(GameItem recipeItem) {
|
||||
// Make sure this is actually a cooking recipe.
|
||||
if (recipeItem.getItemData().getItemUse().get(0).getUseOp() != ItemUseOp.ITEM_USE_UNLOCK_COOK_RECIPE) {
|
||||
return false;
|
||||
public boolean unlockRecipe(int id) {
|
||||
if (this.player.getUnlockedRecipies().containsKey(id)) {
|
||||
return false; // Recipe already unlocked
|
||||
}
|
||||
|
||||
// Determine the recipe we should unlock.
|
||||
int recipeId = Integer.parseInt(recipeItem.getItemData().getItemUse().get(0).getUseParam()[0]);
|
||||
|
||||
// Remove the item from the player's inventory.
|
||||
// We need to do this here, before sending CookRecipeDataNotify, or the the UI won't correctly update.
|
||||
player.getInventory().removeItem(recipeItem, 1);
|
||||
|
||||
// Tell the client that this blueprint is now unlocked and add the unlocked item to the player.
|
||||
this.player.getUnlockedRecipies().put(recipeId, 0);
|
||||
this.player.sendPacket(new PacketCookRecipeDataNotify(recipeId));
|
||||
this.player.getUnlockedRecipies().put(id, 0);
|
||||
this.player.sendPacket(new PacketCookRecipeDataNotify(id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1,12 +1,9 @@
|
||||
package emu.grasscutter.game.managers;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.game.home.FurnitureMakeSlotItem;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.player.BasePlayerManager;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
import emu.grasscutter.net.proto.ItemParamOuterClass;
|
||||
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
@ -34,26 +31,19 @@ public class FurnitureManager extends BasePlayerManager {
|
||||
player.getSession().send(new PacketUnlockedFurnitureSuiteDataNotify(player.getUnlockedFurnitureSuite()));
|
||||
}
|
||||
|
||||
public synchronized boolean unlockFurnitureOrSuite(GameItem useItem) {
|
||||
ItemUseOp itemUseOp = useItem.getItemData().getItemUse().get(0).getUseOp();
|
||||
|
||||
// Check
|
||||
if (itemUseOp != ItemUseOp.ITEM_USE_UNLOCK_FURNITURE_SUITE && itemUseOp != ItemUseOp.ITEM_USE_UNLOCK_FURNITURE_FORMULA) {
|
||||
return false;
|
||||
public boolean unlockFurnitureFormula(int id) {
|
||||
if (!player.getUnlockedFurniture().add(id)) {
|
||||
return false; // Already unlocked!
|
||||
}
|
||||
notifyUnlockFurniture();
|
||||
return true;
|
||||
}
|
||||
|
||||
int furnitureIdOrSuiteId = Integer.parseInt(useItem.getItemData().getItemUse().get(0).getUseParam()[0]);
|
||||
|
||||
// Remove first
|
||||
player.getInventory().removeItem(useItem, 1);
|
||||
|
||||
if (useItem.getItemData().getItemUse().get(0).getUseOp() == ItemUseOp.ITEM_USE_UNLOCK_FURNITURE_FORMULA) {
|
||||
player.getUnlockedFurniture().add(furnitureIdOrSuiteId);
|
||||
notifyUnlockFurniture();
|
||||
}else {
|
||||
player.getUnlockedFurnitureSuite().add(furnitureIdOrSuiteId);
|
||||
notifyUnlockFurnitureSuite();
|
||||
public boolean unlockFurnitureSuite(int id) {
|
||||
if (!player.getUnlockedFurnitureSuite().add(id)) {
|
||||
return false; // Already unlocked!
|
||||
}
|
||||
notifyUnlockFurnitureSuite();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -13,12 +13,17 @@ import emu.grasscutter.game.entity.EntityItem;
|
||||
import emu.grasscutter.game.entity.EntityMonster;
|
||||
import emu.grasscutter.game.entity.GameEntity;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.inventory.MaterialType;
|
||||
import emu.grasscutter.game.player.BasePlayerManager;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ElementType;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
import emu.grasscutter.game.props.ItemUseTarget;
|
||||
import emu.grasscutter.game.props.MonsterType;
|
||||
import emu.grasscutter.game.props.WeaponType;
|
||||
import emu.grasscutter.game.props.ItemUseAction.ItemUseAction;
|
||||
import emu.grasscutter.game.props.ItemUseAction.ItemUseAddEnergy;
|
||||
import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall;
|
||||
import emu.grasscutter.net.proto.AbilityIdentifierOuterClass.AbilityIdentifier;
|
||||
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
||||
@ -30,10 +35,10 @@ import emu.grasscutter.server.game.GameSession;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
@ -42,7 +47,7 @@ import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
public class EnergyManager extends BasePlayerManager {
|
||||
private final Map<EntityAvatar, Integer> avatarNormalProbabilities;
|
||||
private final Object2IntMap<EntityAvatar> avatarNormalProbabilities;
|
||||
|
||||
private boolean energyUsage; // Should energy usage be enabled for this player?
|
||||
private final static Int2ObjectMap<List<EnergyDropInfo>> energyDropData = new Int2ObjectOpenHashMap<>();
|
||||
@ -50,7 +55,7 @@ public class EnergyManager extends BasePlayerManager {
|
||||
|
||||
public EnergyManager(Player player) {
|
||||
super(player);
|
||||
this.avatarNormalProbabilities = new HashMap<>();
|
||||
this.avatarNormalProbabilities = new Object2IntOpenHashMap<>();
|
||||
this.energyUsage=GAME_OPTIONS.energyUsage;
|
||||
}
|
||||
|
||||
@ -170,66 +175,9 @@ public class EnergyManager extends BasePlayerManager {
|
||||
}
|
||||
|
||||
// Generate the particles.
|
||||
var pos = new Position(action.getPos());
|
||||
for (int i = 0; i < amount; i++) {
|
||||
this.generateElemBall(itemId, new Position(action.getPos()), 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pickup of elemental particles and orbs.
|
||||
* @param elemBall The elemental particle or orb.
|
||||
*/
|
||||
public void handlePickupElemBall(GameItem elemBall) {
|
||||
// Check if the item is indeed an energy particle/orb.
|
||||
if (elemBall.getItemId() < 2001 ||elemBall.getItemId() > 2024) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine the base amount of energy given by the particle/orb.
|
||||
// Particles have a base amount of 1.0, and orbs a base amount of 3.0.
|
||||
float baseEnergy = (elemBall.getItemId() <= 2008) ? 3.0f : 1.0f;
|
||||
|
||||
// Add energy to every team member.
|
||||
for (int i = 0; i < this.player.getTeamManager().getActiveTeam().size(); i++) {
|
||||
EntityAvatar entity = this.player.getTeamManager().getActiveTeam().get(i);
|
||||
|
||||
// On-field vs off-field multiplier.
|
||||
// The on-field character gets no penalty.
|
||||
// Off-field characters get a penalty depending on the team size, as follows:
|
||||
// - 2 character team: 0.8
|
||||
// - 3 character team: 0.7
|
||||
// - 4 character team: 0.6
|
||||
// - etc.
|
||||
// We set a lower bound of 0.1 here, to avoid gaining no or negative energy.
|
||||
float offFieldPenalty =
|
||||
(this.player.getTeamManager().getCurrentCharacterIndex() == i)
|
||||
? 1.0f
|
||||
: 1.0f - this.player.getTeamManager().getActiveTeam().size() * 0.1f;
|
||||
offFieldPenalty = Math.max(offFieldPenalty, 0.1f);
|
||||
|
||||
// Same element/neutral bonus.
|
||||
// Same-element characters get a bonus of *3, while different-element characters get no bonus at all.
|
||||
// For neutral particles/orbs, the multiplier is always *2.
|
||||
if (entity.getAvatar().getSkillDepot() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ElementType avatarElement = entity.getAvatar().getSkillDepot().getElementType();
|
||||
ElementType ballElement = switch (elemBall.getItemId()) {
|
||||
case 2001, 2017 -> ElementType.Fire;
|
||||
case 2002, 2018 -> ElementType.Water;
|
||||
case 2003, 2019 -> ElementType.Grass;
|
||||
case 2004, 2020 -> ElementType.Electric;
|
||||
case 2005, 2021 -> ElementType.Wind;
|
||||
case 2006, 2022 -> ElementType.Ice;
|
||||
case 2007, 2023 -> ElementType.Rock;
|
||||
default -> null;
|
||||
};
|
||||
|
||||
float elementBonus = (ballElement == null) ? 2.0f : (avatarElement == ballElement) ? 3.0f : 1.0f;
|
||||
|
||||
// Add the energy.
|
||||
entity.addEnergy(baseEnergy * elementBonus * offFieldPenalty * elemBall.getCount(), PropChangeReason.PROP_CHANGE_REASON_ENERGY_BALL);
|
||||
this.generateElemBall(itemId, pos, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,7 +204,7 @@ public class EnergyManager extends BasePlayerManager {
|
||||
}
|
||||
|
||||
// Roll for energy.
|
||||
int currentProbability = this.avatarNormalProbabilities.get(avatar);
|
||||
int currentProbability = this.avatarNormalProbabilities.getInt(avatar);
|
||||
int roll = ThreadLocalRandom.current().nextInt(0, 100);
|
||||
|
||||
// If the player wins the roll, we increase the avatar's energy and reset the probability.
|
||||
|
@ -5,7 +5,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.excels.ForgeData;
|
||||
@ -14,7 +13,6 @@ import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.player.BasePlayerManager;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
import emu.grasscutter.net.proto.ForgeQueueDataOuterClass.ForgeQueueData;
|
||||
import emu.grasscutter.net.proto.ForgeQueueManipulateReqOuterClass.ForgeQueueManipulateReq;
|
||||
@ -38,24 +36,12 @@ public class ForgingManager extends BasePlayerManager {
|
||||
/**********
|
||||
Blueprint unlocking.
|
||||
**********/
|
||||
public synchronized boolean unlockForgingBlueprint(GameItem blueprintItem) {
|
||||
// Make sure this is actually a forging blueprint.
|
||||
if (blueprintItem.getItemData().getItemUse().get(0).getUseOp() != ItemUseOp.ITEM_USE_UNLOCK_FORGE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine the forging item we should unlock.
|
||||
int forgeId = Integer.parseInt(blueprintItem.getItemData().getItemUse().get(0).getUseParam()[0]);
|
||||
|
||||
// Remove the blueprint from the player's inventory.
|
||||
// We need to do this here, before sending ForgeFormulaDataNotify, or the the forging UI won't correctly
|
||||
// update when unlocking the blueprint.
|
||||
player.getInventory().removeItem(blueprintItem, 1);
|
||||
|
||||
public boolean unlockForgingBlueprint(int id) {
|
||||
// Tell the client that this blueprint is now unlocked and add the unlocked item to the player.
|
||||
this.player.getUnlockedForgingBlueprints().add(forgeId);
|
||||
this.player.sendPacket(new PacketForgeFormulaDataNotify(forgeId));
|
||||
|
||||
if (!this.player.getUnlockedForgingBlueprints().add(id)) {
|
||||
return false; // Already unlocked
|
||||
}
|
||||
this.player.sendPacket(new PacketForgeFormulaDataNotify(id));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -29,49 +29,49 @@ import java.util.*;
|
||||
public class StaminaManager extends BasePlayerManager {
|
||||
|
||||
// TODO: Skiff state detection?
|
||||
private static final HashMap<String, HashSet<MotionState>> MotionStatesCategorized = new HashMap<>() {{
|
||||
put("CLIMB", new HashSet<>(List.of(
|
||||
private static final Map<String, Set<MotionState>> MotionStatesCategorized = new HashMap<>() {{
|
||||
put("CLIMB", Set.of(
|
||||
MotionState.MOTION_STATE_CLIMB, // sustained, when not moving no cost no recover
|
||||
MotionState.MOTION_STATE_STANDBY_TO_CLIMB // NOT OBSERVED, see MOTION_JUMP_UP_WALL_FOR_STANDBY
|
||||
)));
|
||||
put("DASH", new HashSet<>(List.of(
|
||||
));
|
||||
put("DASH", Set.of(
|
||||
MotionState.MOTION_STATE_DANGER_DASH, // sustained
|
||||
MotionState.MOTION_STATE_DASH // sustained
|
||||
)));
|
||||
put("FLY", new HashSet<>(List.of(
|
||||
));
|
||||
put("FLY", Set.of(
|
||||
MotionState.MOTION_STATE_FLY, // sustained
|
||||
MotionState.MOTION_STATE_FLY_FAST, // sustained
|
||||
MotionState.MOTION_STATE_FLY_SLOW, // sustained
|
||||
MotionState.MOTION_STATE_POWERED_FLY // sustained, recover
|
||||
)));
|
||||
put("RUN", new HashSet<>(List.of(
|
||||
));
|
||||
put("RUN", Set.of(
|
||||
MotionState.MOTION_STATE_DANGER_RUN, // sustained, recover
|
||||
MotionState.MOTION_STATE_RUN // sustained, recover
|
||||
)));
|
||||
put("SKIFF", new HashSet<>(List.of(
|
||||
));
|
||||
put("SKIFF", Set.of(
|
||||
MotionState.MOTION_STATE_SKIFF_BOARDING, // NOT OBSERVED even when boarding
|
||||
MotionState.MOTION_STATE_SKIFF_DASH, // sustained, observed with waverider entity ID.
|
||||
MotionState.MOTION_STATE_SKIFF_NORMAL, // sustained, OBSERVED when both normal and dashing
|
||||
MotionState.MOTION_STATE_SKIFF_POWERED_DASH // sustained, recover
|
||||
)));
|
||||
put("STANDBY", new HashSet<>(List.of(
|
||||
));
|
||||
put("STANDBY", Set.of(
|
||||
MotionState.MOTION_STATE_DANGER_STANDBY_MOVE, // sustained, recover
|
||||
MotionState.MOTION_STATE_DANGER_STANDBY, // sustained, recover
|
||||
MotionState.MOTION_STATE_LADDER_TO_STANDBY, // NOT OBSERVED
|
||||
MotionState.MOTION_STATE_STANDBY_MOVE, // sustained, recover
|
||||
MotionState.MOTION_STATE_STANDBY // sustained, recover
|
||||
)));
|
||||
put("SWIM", new HashSet<>(List.of(
|
||||
));
|
||||
put("SWIM", Set.of(
|
||||
MotionState.MOTION_STATE_SWIM_IDLE, // sustained
|
||||
MotionState.MOTION_STATE_SWIM_DASH, // immediate and sustained
|
||||
MotionState.MOTION_STATE_SWIM_JUMP, // NOT OBSERVED
|
||||
MotionState.MOTION_STATE_SWIM_MOVE // sustained
|
||||
)));
|
||||
put("WALK", new HashSet<>(List.of(
|
||||
));
|
||||
put("WALK", Set.of(
|
||||
MotionState.MOTION_STATE_DANGER_WALK, // sustained, recover
|
||||
MotionState.MOTION_STATE_WALK // sustained, recover
|
||||
)));
|
||||
put("OTHER", new HashSet<>(List.of(
|
||||
));
|
||||
put("OTHER", Set.of(
|
||||
MotionState.MOTION_STATE_CLIMB_JUMP, // cost only once if repeated without switching state
|
||||
MotionState.MOTION_STATE_DASH_BEFORE_SHAKE, // immediate one time sprint charge.
|
||||
MotionState.MOTION_STATE_FIGHT, // immediate, if sustained then subsequent will be MOTION_NOTIFY
|
||||
@ -79,13 +79,13 @@ public class StaminaManager extends BasePlayerManager {
|
||||
MotionState.MOTION_STATE_NOTIFY, // can be either cost or recover - check previous state and check skill casting
|
||||
MotionState.MOTION_STATE_SIT_IDLE, // sustained, recover
|
||||
MotionState.MOTION_STATE_JUMP // recover
|
||||
)));
|
||||
put("NOCOST_NORECOVER", new HashSet<>(List.of(
|
||||
));
|
||||
put("NOCOST_NORECOVER", Set.of(
|
||||
MotionState.MOTION_STATE_LADDER_SLIP, // NOT OBSERVED
|
||||
MotionState.MOTION_STATE_SLIP, // sustained, no cost no recover
|
||||
MotionState.MOTION_STATE_FLY_IDLE // NOT OBSERVED
|
||||
)));
|
||||
put("IGNORE", new HashSet<>(List.of(
|
||||
));
|
||||
put("IGNORE", Set.of(
|
||||
// these states have no impact on stamina
|
||||
MotionState.MOTION_STATE_CROUCH_IDLE,
|
||||
MotionState.MOTION_STATE_CROUCH_MOVE,
|
||||
@ -106,7 +106,7 @@ public class StaminaManager extends BasePlayerManager {
|
||||
MotionState.MOTION_STATE_RESET,
|
||||
MotionState.MOTION_STATE_STANDBY_TO_LADDER,
|
||||
MotionState.MOTION_STATE_WATERFALL
|
||||
)));
|
||||
));
|
||||
}};
|
||||
|
||||
private final Logger logger = Grasscutter.getLogger();
|
||||
@ -127,9 +127,7 @@ public class StaminaManager extends BasePlayerManager {
|
||||
private boolean lastSkillFirstTick = true;
|
||||
private int vehicleId = -1;
|
||||
private int vehicleStamina = GlobalVehicleMaxStamina;
|
||||
private static final HashSet<Integer> TalentMovements = new HashSet<>(List.of(
|
||||
10013, 10413
|
||||
));
|
||||
private static final Set<Integer> TalentMovements = Set.of(10013, 10413);
|
||||
private static final HashMap<Integer, Float> ClimbFoodReductionMap = new HashMap<>() {{
|
||||
// TODO: get real food id
|
||||
put(0, 0.8f); // Sample food
|
||||
@ -190,6 +188,17 @@ public class StaminaManager extends BasePlayerManager {
|
||||
return vehicleStamina;
|
||||
}
|
||||
|
||||
public boolean addCurrentStamina(int amount) {
|
||||
var cur = this.getCurrentCharacterStamina();
|
||||
var max = this.getMaxCharacterStamina();
|
||||
if (cur >= max) return false;
|
||||
var value = cur + amount;
|
||||
if (value > max)
|
||||
value = max;
|
||||
this.player.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean registerBeforeUpdateStaminaListener(String listenerName, BeforeUpdateStaminaListener listener) {
|
||||
if (beforeUpdateStaminaListeners.containsKey(listenerName)) {
|
||||
return false;
|
||||
@ -405,27 +414,17 @@ public class StaminaManager extends BasePlayerManager {
|
||||
// Internal handler
|
||||
|
||||
private void handleImmediateStamina(GameSession session, @NotNull MotionState motionState) {
|
||||
if (currentState == motionState) return;
|
||||
switch (motionState) {
|
||||
case MOTION_STATE_CLIMB:
|
||||
if (currentState != MotionState.MOTION_STATE_CLIMB) {
|
||||
case MOTION_STATE_CLIMB ->
|
||||
updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_START), true);
|
||||
}
|
||||
break;
|
||||
case MOTION_STATE_DASH_BEFORE_SHAKE:
|
||||
if (previousState != MotionState.MOTION_STATE_DASH_BEFORE_SHAKE) {
|
||||
case MOTION_STATE_DASH_BEFORE_SHAKE ->
|
||||
updateStaminaRelative(session, new Consumption(ConsumptionType.SPRINT), true);
|
||||
}
|
||||
break;
|
||||
case MOTION_STATE_CLIMB_JUMP:
|
||||
if (previousState != MotionState.MOTION_STATE_CLIMB_JUMP) {
|
||||
case MOTION_STATE_CLIMB_JUMP ->
|
||||
updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_JUMP), true);
|
||||
}
|
||||
break;
|
||||
case MOTION_STATE_SWIM_DASH:
|
||||
if (previousState != MotionState.MOTION_STATE_SWIM_DASH) {
|
||||
case MOTION_STATE_SWIM_DASH ->
|
||||
updateStaminaRelative(session, new Consumption(ConsumptionType.SWIM_DASH_START), true);
|
||||
}
|
||||
break;
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -526,20 +525,14 @@ public class StaminaManager extends BasePlayerManager {
|
||||
// Bow avatar charged attack
|
||||
Avatar currentAvatar = player.getTeamManager().getCurrentAvatarEntity().getAvatar();
|
||||
|
||||
switch (currentAvatar.getAvatarData().getWeaponType()) {
|
||||
case WEAPON_BOW:
|
||||
return getBowSustainedCost(skillCasting);
|
||||
case WEAPON_CLAYMORE:
|
||||
return getClaymoreSustainedCost(skillCasting);
|
||||
case WEAPON_CATALYST:
|
||||
return getCatalystCost(skillCasting);
|
||||
case WEAPON_POLE:
|
||||
return getPolearmCost(skillCasting);
|
||||
case WEAPON_SWORD_ONE_HAND:
|
||||
return getSwordCost(skillCasting);
|
||||
}
|
||||
|
||||
return new Consumption();
|
||||
return switch (currentAvatar.getAvatarData().getWeaponType()) {
|
||||
case WEAPON_BOW -> getBowSustainedCost(skillCasting);
|
||||
case WEAPON_CLAYMORE -> getClaymoreSustainedCost(skillCasting);
|
||||
case WEAPON_CATALYST -> getCatalystCost(skillCasting);
|
||||
case WEAPON_POLE -> getPolearmCost(skillCasting);
|
||||
case WEAPON_SWORD_ONE_HAND -> getSwordCost(skillCasting);
|
||||
default -> new Consumption();
|
||||
};
|
||||
}
|
||||
|
||||
private Consumption getClimbConsumption() {
|
||||
|
@ -653,7 +653,8 @@ public class Player {
|
||||
return (int) ((theLastDay.getTime() - now.getTime()) / (24 * 60 * 60 * 1000)); // By copilot
|
||||
}
|
||||
|
||||
public void rechargeMoonCard() {
|
||||
public boolean rechargeMoonCard() {
|
||||
if (this.moonCardDuration > 150) return false; // Can only stack up to 180 days
|
||||
inventory.addItem(new GameItem(203, 300));
|
||||
if (!moonCard) {
|
||||
moonCard = true;
|
||||
@ -666,6 +667,7 @@ public class Player {
|
||||
if (!moonCardGetTimes.contains(moonCardStartTime)) {
|
||||
moonCardGetTimes.add(moonCardStartTime);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void getTodayMoonCard() {
|
||||
|
@ -2,9 +2,12 @@ package emu.grasscutter.game.player;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.BuffData;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.net.proto.ServerBuffChangeNotifyOuterClass.ServerBuffChangeNotify.ServerBuffChangeType;
|
||||
import emu.grasscutter.net.proto.ServerBuffOuterClass.ServerBuff;
|
||||
import emu.grasscutter.server.packet.send.PacketServerBuffChangeNotify;
|
||||
@ -14,6 +17,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
|
||||
@Getter(AccessLevel.PRIVATE)
|
||||
public class PlayerBuffManager extends BasePlayerManager {
|
||||
@ -74,10 +78,50 @@ public class PlayerBuffManager extends BasePlayerManager {
|
||||
* @return True if a buff was added
|
||||
*/
|
||||
public synchronized boolean addBuff(int buffId, float duration) {
|
||||
return addBuff(buffId, duration, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a server buff to the player.
|
||||
* @param buffId Server buff id
|
||||
* @param duration Duration of the buff in seconds. Set to 0 for an infinite buff.
|
||||
* @param target Target avatar
|
||||
* @return True if a buff was added
|
||||
*/
|
||||
public synchronized boolean addBuff(int buffId, float duration, Avatar target) {
|
||||
// Get buff excel data
|
||||
BuffData buffData = GameData.getBuffDataMap().get(buffId);
|
||||
if (buffData == null) return false;
|
||||
|
||||
boolean success = false;
|
||||
|
||||
// Perform onAdded actions
|
||||
success |= Optional.ofNullable(GameData.getAbilityData(buffData.getAbilityName()))
|
||||
.map(data -> data.modifiers.get(buffData.getModifierName()))
|
||||
.map(modifier -> modifier.onAdded)
|
||||
.map(onAdded -> {
|
||||
System.out.println("onAdded exists");
|
||||
boolean s = false;
|
||||
for (var a: onAdded) {
|
||||
switch (a.type) {
|
||||
case HealHP:
|
||||
System.out.println("Attempting heal");
|
||||
if (target == null) break;
|
||||
float maxHp = target.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||
float amount = a.amount.get() + a.amountByTargetMaxHPRatio.get() * maxHp;
|
||||
target.getAsEntity().heal(amount);
|
||||
s = true;
|
||||
System.out.printf("Healed {}", amount);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
})
|
||||
.orElse(false);
|
||||
System.out.println("Oh no");
|
||||
|
||||
// Set duration
|
||||
if (duration < 0f) {
|
||||
duration = buffData.getTime();
|
||||
@ -85,7 +129,7 @@ public class PlayerBuffManager extends BasePlayerManager {
|
||||
|
||||
// Dont add buff if duration is equal or less than 0
|
||||
if (duration <= 0) {
|
||||
return false;
|
||||
return success;
|
||||
}
|
||||
|
||||
// Clear previous buff if it exists
|
||||
|
@ -568,10 +568,7 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
entity.setFightProperty(
|
||||
FightProperty.FIGHT_PROP_CUR_HP,
|
||||
entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * .1f
|
||||
);
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 1f);
|
||||
this.getPlayer().sendPacket(new PacketAvatarFightPropUpdateNotify(entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP));
|
||||
this.getPlayer().sendPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar()));
|
||||
return true;
|
||||
|
@ -0,0 +1,19 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseAcceptQuest extends ItemUseInt {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_ACCEPT_QUEST;
|
||||
}
|
||||
|
||||
public ItemUseAcceptQuest(String[] useParam) {
|
||||
super(useParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
return (params.player.getQuestManager().addQuest(this.i) != null);
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.data.common.ItemUseData;
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseAction {
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_NONE;
|
||||
}
|
||||
|
||||
public static ItemUseAction fromItemUseData(ItemUseData data) {
|
||||
var useParam = data.getUseParam();
|
||||
return switch (data.getUseOp()) {
|
||||
case ITEM_USE_NONE -> null;
|
||||
// Uprade materials - no direct usage
|
||||
case ITEM_USE_ADD_EXP -> new ItemUseAddExp(useParam);
|
||||
case ITEM_USE_ADD_RELIQUARY_EXP -> new ItemUseAddReliquaryExp(useParam);
|
||||
case ITEM_USE_ADD_WEAPON_EXP -> new ItemUseAddWeaponExp(useParam);
|
||||
// Energy pickups
|
||||
case ITEM_USE_ADD_ALL_ENERGY -> new ItemUseAddAllEnergy(useParam);
|
||||
case ITEM_USE_ADD_ELEM_ENERGY -> new ItemUseAddElemEnergy(useParam);
|
||||
// Give items
|
||||
case ITEM_USE_ADD_ITEM -> new ItemUseAddItem(useParam);
|
||||
case ITEM_USE_GAIN_AVATAR -> new ItemUseGainAvatar(useParam);
|
||||
case ITEM_USE_GAIN_COSTUME -> new ItemUseGainCostume(useParam); // TODO - real success/fail
|
||||
case ITEM_USE_GAIN_FLYCLOAK -> new ItemUseGainFlycloak(useParam); // TODO - real success/fail
|
||||
case ITEM_USE_GAIN_NAME_CARD -> new ItemUseGainNameCard(useParam); // TODO
|
||||
case ITEM_USE_CHEST_SELECT_ITEM -> new ItemUseChestSelectItem(useParam);
|
||||
case ITEM_USE_ADD_SELECT_ITEM -> new ItemUseAddSelectItem(useParam);
|
||||
case ITEM_USE_GRANT_SELECT_REWARD -> new ItemUseGrantSelectReward(useParam);
|
||||
case ITEM_USE_COMBINE_ITEM -> new ItemUseCombineItem(useParam);
|
||||
case ITEM_USE_OPEN_RANDOM_CHEST -> new ItemUseOpenRandomChest(useParam);
|
||||
// Food effects
|
||||
case ITEM_USE_RELIVE_AVATAR -> new ItemUseReliveAvatar(useParam); // First action for revival food. Should we worry about race conditions in parallel streams?
|
||||
case ITEM_USE_ADD_CUR_HP -> new ItemUseAddCurHp(useParam);
|
||||
case ITEM_USE_ADD_CUR_STAMINA -> new ItemUseAddCurStamina(useParam);
|
||||
case ITEM_USE_ADD_SERVER_BUFF -> new ItemUseAddServerBuff(useParam);
|
||||
case ITEM_USE_MAKE_GADGET -> new ItemUseMakeGadget(useParam);
|
||||
// Unlock recipes - TODO: allow scheduling packets for after recipe is removed
|
||||
case ITEM_USE_UNLOCK_COMBINE -> new ItemUseUnlockCombine(useParam);
|
||||
case ITEM_USE_UNLOCK_CODEX -> new ItemUseUnlockCodex(useParam); // TODO: No backend for this yet
|
||||
case ITEM_USE_UNLOCK_COOK_RECIPE -> new ItemUseUnlockCookRecipe(useParam);
|
||||
case ITEM_USE_UNLOCK_FORGE -> new ItemUseUnlockForge(useParam);
|
||||
case ITEM_USE_UNLOCK_FURNITURE_FORMULA -> new ItemUseUnlockFurnitureFormula(useParam);
|
||||
case ITEM_USE_UNLOCK_FURNITURE_SUITE -> new ItemUseUnlockFurnitureSuite(useParam);
|
||||
case ITEM_USE_UNLOCK_HOME_MODULE -> new ItemUseUnlockHomeModule(useParam); // No backend for this yet
|
||||
case ITEM_USE_UNLOCK_HOME_BGM -> new ItemUseUnlockHomeBgm(useParam);
|
||||
// Account things
|
||||
case ITEM_USE_ACCEPT_QUEST -> new ItemUseAcceptQuest(useParam);
|
||||
case ITEM_USE_GAIN_CARD_PRODUCT -> new ItemUseGainCardProduct(useParam);
|
||||
case ITEM_USE_UNLOCK_PAID_BATTLE_PASS_NORMAL -> new ItemUseUnlockPaidBattlePassNormal(useParam); // TODO: add paid BP
|
||||
|
||||
// Unused in current resources
|
||||
case ITEM_USE_DEL_SERVER_BUFF -> null;
|
||||
case ITEM_USE_ADD_BIG_TALENT_POINT -> null;
|
||||
case ITEM_USE_GAIN_RESIN_CARD_PRODUCT -> null;
|
||||
case ITEM_USE_TRIGGER_ABILITY -> null;
|
||||
case ITEM_USE_ADD_TREASURE_MAP_BONUS_REGION_FRAGMENT -> null;
|
||||
// Used in current resources but no point yet
|
||||
case ITEM_USE_ADD_PERSIST_STAMINA -> null; // [int amount] one Test item
|
||||
case ITEM_USE_ADD_TEMPORARY_STAMINA -> null; // [int amount] one Test item
|
||||
case ITEM_USE_ADD_DUNGEON_COND_TIME -> null; // [int 1, int 15 or 20] - minigame shards
|
||||
case ITEM_USE_ADD_CHANNELLER_SLAB_BUFF -> null; // [int] minigame buffs
|
||||
case ITEM_USE_ADD_REGIONAL_PLAY_VAR -> null; // [String, int] - coral butterfly effect
|
||||
};
|
||||
}
|
||||
|
||||
public boolean useItem(UseItemParams params) {
|
||||
// An item must return true on at least one of its actions to count as successfully used.
|
||||
// If all of the actions return false, the item will not be consumed from inventory.
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean postUseItem(UseItemParams params) {
|
||||
// This is run after the item has been consumed from inventory.
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ElementType;
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseAddAllEnergy extends ItemUseAddEnergy {
|
||||
private float energy = 0f;
|
||||
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_ADD_ALL_ENERGY;
|
||||
}
|
||||
|
||||
public ItemUseAddAllEnergy(String[] useParam) {
|
||||
try {
|
||||
this.energy = Float.parseFloat(useParam[0]);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
public float getAddEnergy(ElementType avatarElement) {
|
||||
return this.energy;
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseAddCurHp extends ItemUseInt {
|
||||
private String icon;
|
||||
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_ADD_CUR_HP;
|
||||
}
|
||||
|
||||
public ItemUseAddCurHp(String[] useParam) {
|
||||
super(useParam);
|
||||
this.icon = useParam[1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
return (params.targetAvatar.getAsEntity().heal(params.count * this.i) > 0.01);
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseAddCurStamina extends ItemUseInt {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_ADD_CUR_STAMINA;
|
||||
}
|
||||
|
||||
public ItemUseAddCurStamina(String[] useParam) {
|
||||
super(useParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
return params.player.getStaminaManager().addCurrentStamina(this.i);
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ElementType;
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseAddElemEnergy extends ItemUseAddEnergy {
|
||||
private ElementType element = ElementType.None;
|
||||
private float elemEnergy = 0f;
|
||||
private float otherEnergy = 0f;
|
||||
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_ADD_ELEM_ENERGY;
|
||||
}
|
||||
|
||||
public ItemUseAddElemEnergy(String[] useParam) {
|
||||
try {
|
||||
this.element = ElementType.getTypeByValue(Integer.parseInt(useParam[0]));
|
||||
} catch (Exception ignored) {}
|
||||
try {
|
||||
this.elemEnergy = Float.parseFloat(useParam[1]);
|
||||
} catch (Exception ignored) {}
|
||||
try {
|
||||
this.otherEnergy = Float.parseFloat(useParam[2]);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
public float getAddEnergy(ElementType avatarElement) {
|
||||
return (avatarElement == this.element) ? this.elemEnergy : this.otherEnergy;
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.data.excels.AvatarSkillDepotData;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.props.ElementType;
|
||||
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
||||
|
||||
public abstract class ItemUseAddEnergy extends ItemUseAction {
|
||||
public abstract float getAddEnergy(ElementType avatarElement);
|
||||
|
||||
public float getAddEnergy(AvatarSkillDepotData depot) {
|
||||
if (depot == null) return 0f;
|
||||
var element = depot.getElementType();
|
||||
if (element == null) return 0f;
|
||||
return this.getAddEnergy(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
var teamManager = params.player.getTeamManager();
|
||||
return switch (params.itemUseTarget) {
|
||||
case ITEM_USE_TARGET_CUR_AVATAR -> {
|
||||
this.addEnergy(teamManager.getCurrentAvatarEntity().getAvatar(), params.count);
|
||||
yield true; // Always consume elem balls
|
||||
}
|
||||
case ITEM_USE_TARGET_CUR_TEAM -> {
|
||||
var activeTeam = teamManager.getActiveTeam();
|
||||
// On-field vs off-field multiplier.
|
||||
// The on-field character gets full amount, off-field characters get less depending on the team size.
|
||||
final float offFieldRatio = switch(activeTeam.size()) {
|
||||
case 2 -> 0.8f;
|
||||
case 3 -> 0.7f;
|
||||
default -> 0.6f;
|
||||
};
|
||||
final int currentCharacterIndex = teamManager.getCurrentCharacterIndex();
|
||||
|
||||
// Add energy to every team member.
|
||||
for (int i = 0; i < activeTeam.size(); i++) {
|
||||
var avatar = activeTeam.get(i).getAvatar();
|
||||
if (i == currentCharacterIndex)
|
||||
this.addEnergy(avatar, params.count);
|
||||
else
|
||||
this.addEnergy(avatar, params.count * offFieldRatio);
|
||||
}
|
||||
|
||||
yield true; // Always consume elem balls
|
||||
}
|
||||
case ITEM_USE_TARGET_SPECIFY_AVATAR, ITEM_USE_TARGET_SPECIFY_ALIVE_AVATAR, ITEM_USE_TARGET_SPECIFY_DEAD_AVATAR ->
|
||||
this.addEnergy(params.targetAvatar, params.count); // Targeted items might care about this
|
||||
case ITEM_USE_TARGET_NONE -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private boolean addEnergy(Avatar avatar, float multiplier) {
|
||||
float energy = this.getAddEnergy(avatar.getSkillDepot()) * multiplier;
|
||||
if (energy < 0.01f)
|
||||
return false;
|
||||
avatar.getAsEntity().addEnergy(energy, PropChangeReason.PROP_CHANGE_REASON_ENERGY_BALL);
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
import lombok.Getter;
|
||||
|
||||
public class ItemUseAddExp extends ItemUseAction {
|
||||
@Getter private int exp = 0;
|
||||
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_ADD_EXP;
|
||||
}
|
||||
|
||||
public ItemUseAddExp(String[] useParam) {
|
||||
try {
|
||||
this.exp = Integer.parseInt(useParam[0]);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseAddItem extends ItemUseInt {
|
||||
private int count = 0;
|
||||
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_ADD_ITEM;
|
||||
}
|
||||
|
||||
public ItemUseAddItem(String[] useParam) {
|
||||
super(useParam);
|
||||
try {
|
||||
this.count = Integer.parseInt(useParam[1]);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
return params.player.getInventory().addItem(this.i, this.count * params.count);
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
import lombok.Getter;
|
||||
|
||||
public class ItemUseAddReliquaryExp extends ItemUseAction {
|
||||
@Getter private int exp = 0;
|
||||
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_ADD_RELIQUARY_EXP;
|
||||
}
|
||||
|
||||
public ItemUseAddReliquaryExp(String[] useParam) {
|
||||
try {
|
||||
this.exp = Integer.parseInt(useParam[0]);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseAddSelectItem extends ItemUseSelectItems {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_ADD_SELECT_ITEM;
|
||||
}
|
||||
|
||||
public ItemUseAddSelectItem(String[] useParam) {
|
||||
String[] options = useParam[0].split(",");
|
||||
this.optionItemIds = new int[options.length];
|
||||
for (int i = 0; i < options.length; i++) {
|
||||
try {
|
||||
this.optionItemIds[i] = Integer.parseInt(options[i]);
|
||||
} catch (NumberFormatException ignored) {
|
||||
this.optionItemIds[i] = INVALID;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseAddServerBuff extends ItemUseInt {
|
||||
private int duration = 0;
|
||||
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_ADD_SERVER_BUFF;
|
||||
}
|
||||
|
||||
public ItemUseAddServerBuff(String[] useParam) {
|
||||
super(useParam);
|
||||
try {
|
||||
this.duration = Integer.parseInt(useParam[1]);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
return params.player.getBuffManager().addBuff(this.i, this.duration, params.targetAvatar);
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
import lombok.Getter;
|
||||
|
||||
public class ItemUseAddWeaponExp extends ItemUseAction {
|
||||
@Getter private int exp = 0;
|
||||
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_ADD_WEAPON_EXP;
|
||||
}
|
||||
|
||||
public ItemUseAddWeaponExp(String[] useParam) {
|
||||
try {
|
||||
this.exp = Integer.parseInt(useParam[0]);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseChestSelectItem extends ItemUseSelectItems {
|
||||
private int[] optionItemCounts;
|
||||
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_CHEST_SELECT_ITEM;
|
||||
}
|
||||
|
||||
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
|
||||
protected int getItemCount(int index) {
|
||||
if ((optionItemCounts == null) || (index < 0) || (index > optionItemCounts.length)) return INVALID;
|
||||
return this.optionItemCounts[index];
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseCombineItem extends ItemUseInt {
|
||||
private int resultId = 0;
|
||||
private int resultCount = 1;
|
||||
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_COMBINE_ITEM;
|
||||
}
|
||||
|
||||
public ItemUseCombineItem(String[] useParam) {
|
||||
super(useParam);
|
||||
try {
|
||||
this.resultId = Integer.parseInt(useParam[1]);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
try {
|
||||
this.resultCount = Integer.parseInt(useParam[2]);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
if (params.count != this.i) return false; // Wrong amount of fragments supplied!
|
||||
return params.player.getInventory().addItem(this.resultId, this.resultCount);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
import emu.grasscutter.game.systems.InventorySystem;
|
||||
|
||||
public class ItemUseGainAvatar extends ItemUseInt {
|
||||
private int level = 1;
|
||||
private int constellation = 0;
|
||||
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_GAIN_AVATAR;
|
||||
}
|
||||
|
||||
public ItemUseGainAvatar(String[] useParam) {
|
||||
super(useParam);
|
||||
try {
|
||||
this.level = Integer.parseInt(useParam[1]);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
try {
|
||||
this.constellation = Integer.parseInt(useParam[2]);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
int haveConstellation = InventorySystem.checkPlayerAvatarConstellationLevel(params.player, this.i);
|
||||
if (haveConstellation == -2 || haveConstellation >= 6) {
|
||||
return false;
|
||||
} else if (haveConstellation == -1) {
|
||||
var avatar = new Avatar(this.i);
|
||||
avatar.setLevel(this.level);
|
||||
avatar.forceConstellationLevel(this.constellation);
|
||||
avatar.recalcStats();
|
||||
params.player.addAvatar(avatar);
|
||||
return true;
|
||||
} else {
|
||||
int itemId = (this.i % 1000) + 100;
|
||||
return params.player.getInventory().addItem(itemId);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseGainCardProduct extends ItemUseAction {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_GAIN_CARD_PRODUCT;
|
||||
}
|
||||
|
||||
public ItemUseGainCardProduct(String[] useParam) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
return params.player.rechargeMoonCard();
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseGainCostume extends ItemUseInt {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_GAIN_COSTUME;
|
||||
}
|
||||
|
||||
public ItemUseGainCostume(String[] useParam) {
|
||||
super(useParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
params.player.getInventory().addItem(this.i); // TODO: Currently this returns false for all virtual items - need to have a proper success/fail
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseGainFlycloak extends ItemUseInt {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_GAIN_FLYCLOAK;
|
||||
}
|
||||
|
||||
public ItemUseGainFlycloak(String[] useParam) {
|
||||
super(useParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
params.player.getInventory().addItem(this.i); // TODO: Currently this returns false for all virtual items - need to have a proper success/fail
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseGainNameCard extends ItemUseAction {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_GAIN_NAME_CARD;
|
||||
}
|
||||
|
||||
public ItemUseGainNameCard(String[] useParam) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
return false; // TODO: work out if this is actually used and how to get the namecard id
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseGrantSelectReward extends ItemUseSelectItems {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_ADD_SELECT_ITEM;
|
||||
}
|
||||
|
||||
public ItemUseGrantSelectReward(String[] useParam) {
|
||||
String[] options = useParam[0].split(",");
|
||||
this.optionItemIds = new int[options.length];
|
||||
for (int i = 0; i < options.length; i++) {
|
||||
try {
|
||||
this.optionItemIds[i] = Integer.parseInt(options[i]);
|
||||
} catch (NumberFormatException ignored) {
|
||||
this.optionItemIds[i] = INVALID;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public abstract class ItemUseInt extends ItemUseAction {
|
||||
@Getter protected int i = 0;
|
||||
|
||||
public ItemUseInt(String[] useParam) {
|
||||
try {
|
||||
this.i = Integer.parseInt(useParam[0]);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.entity.EntityVehicle;
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseMakeGadget extends ItemUseInt {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_MAKE_GADGET;
|
||||
}
|
||||
|
||||
public ItemUseMakeGadget(String[] useParam) {
|
||||
super(useParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
var player = params.player;
|
||||
var scene = player.getScene();
|
||||
var pos = player.getPosition().nearby2d(1f);
|
||||
var rot = player.getRotation().clone();
|
||||
var e = new EntityVehicle(scene, player, this.i, 0, pos, rot);
|
||||
scene.addEntity(e);
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseOpenRandomChest extends ItemUseInt {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_OPEN_RANDOM_CHEST;
|
||||
}
|
||||
|
||||
public ItemUseOpenRandomChest(String[] useParam) {
|
||||
super(useParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) { // cash shop material bundles
|
||||
var rewardItems = params.player.getServer().getShopSystem().getShopChestData(this.i).stream().map(GameItem::new).toList();
|
||||
if (!rewardItems.isEmpty()) {
|
||||
params.player.getInventory().addItems(rewardItems, ActionReason.Shop);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseReliveAvatar extends ItemUseAction {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_RELIVE_AVATAR;
|
||||
}
|
||||
|
||||
public ItemUseReliveAvatar(String[] useParam) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
return params.player.getTeamManager().reviveAvatar(params.targetAvatar);
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
|
||||
public abstract class ItemUseSelectItems extends ItemUseAction {
|
||||
protected static final int INVALID = -1;
|
||||
protected int[] optionItemIds;
|
||||
|
||||
protected int getItemId(int index) {
|
||||
if ((optionItemIds == null) || (index < 0) || (index > optionItemIds.length)) return INVALID;
|
||||
return this.optionItemIds[index];
|
||||
}
|
||||
|
||||
protected int getItemCount(int index) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
protected GameItem getItemStack(int index, int useCount) {
|
||||
int id = this.getItemId(index);
|
||||
int count = this.getItemCount(index);
|
||||
if (id == INVALID || count == INVALID) return null;
|
||||
|
||||
var item = GameData.getItemDataMap().get(id);
|
||||
if (item == null) return null;
|
||||
|
||||
return new GameItem(item, count * useCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
var itemStack = this.getItemStack(params.optionId - 1, params.count);
|
||||
if (itemStack == null) return false;
|
||||
|
||||
return params.player.getInventory().addItem(itemStack, ActionReason.Shop);
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseUnlockCodex extends ItemUseInt {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_UNLOCK_CODEX;
|
||||
}
|
||||
|
||||
public ItemUseUnlockCodex(String[] useParam) {
|
||||
super(useParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseUnlockCombine extends ItemUseInt {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_UNLOCK_COMBINE;
|
||||
}
|
||||
|
||||
public ItemUseUnlockCombine(String[] useParam) {
|
||||
super(useParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean postUseItem(UseItemParams params) {
|
||||
return params.player.getServer().getCombineSystem().unlockCombineDiagram(params.player, this.i);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseUnlockCookRecipe extends ItemUseInt {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_UNLOCK_COOK_RECIPE;
|
||||
}
|
||||
|
||||
public ItemUseUnlockCookRecipe(String[] useParam) {
|
||||
super(useParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean postUseItem(UseItemParams params) {
|
||||
return params.player.getCookingManager().unlockRecipe(this.i);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseUnlockForge extends ItemUseInt {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_UNLOCK_FORGE;
|
||||
}
|
||||
|
||||
public ItemUseUnlockForge(String[] useParam) {
|
||||
super(useParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean postUseItem(UseItemParams params) {
|
||||
return params.player.getForgingManager().unlockForgingBlueprint(this.i);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseUnlockFurnitureFormula extends ItemUseInt {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_UNLOCK_FURNITURE_FORMULA;
|
||||
}
|
||||
|
||||
public ItemUseUnlockFurnitureFormula(String[] useParam) {
|
||||
super(useParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean postUseItem(UseItemParams params) {
|
||||
return params.player.getFurnitureManager().unlockFurnitureFormula(this.i);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseUnlockFurnitureSuite extends ItemUseInt {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_UNLOCK_FURNITURE_SUITE;
|
||||
}
|
||||
|
||||
public ItemUseUnlockFurnitureSuite(String[] useParam) {
|
||||
super(useParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean postUseItem(UseItemParams params) {
|
||||
return params.player.getFurnitureManager().unlockFurnitureSuite(this.i);
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseUnlockHomeBgm extends ItemUseInt {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_UNLOCK_HOME_BGM;
|
||||
}
|
||||
|
||||
public ItemUseUnlockHomeBgm(String[] useParam) {
|
||||
super(useParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
params.player.getHome().addUnlockedHomeBgm(this.i);
|
||||
return true; // Probably best to remove the item even if the bgm was already unlocked.
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseUnlockHomeModule extends ItemUseInt {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_UNLOCK_HOME_MODULE;
|
||||
}
|
||||
|
||||
public ItemUseUnlockHomeModule(String[] useParam) {
|
||||
super(useParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
|
||||
public class ItemUseUnlockPaidBattlePassNormal extends ItemUseAction {
|
||||
@Override
|
||||
public ItemUseOp getItemUseOp() {
|
||||
return ItemUseOp.ITEM_USE_UNLOCK_PAID_BATTLE_PASS_NORMAL;
|
||||
}
|
||||
|
||||
public ItemUseUnlockPaidBattlePassNormal(String[] useParam) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useItem(UseItemParams params) {
|
||||
// TODO: add paid BP
|
||||
//return params.player.getBattlePassManager().setPaid(true);
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ItemUseTarget;
|
||||
|
||||
public class UseItemParams {
|
||||
// Don't want to change 40 method signatures when this gets extended!
|
||||
public Player player;
|
||||
public ItemUseTarget itemUseTarget;
|
||||
public Avatar targetAvatar = null;
|
||||
public int count = 1;
|
||||
public int optionId = 0;
|
||||
public boolean isEnterMpDungeonTeam = false;
|
||||
|
||||
public UseItemParams(Player player, ItemUseTarget itemUseTarget, Avatar targetAvatar, int count, int optionId, boolean isEnterMpDungeonTeam) {
|
||||
this.player = player;
|
||||
this.itemUseTarget = itemUseTarget;
|
||||
this.targetAvatar = targetAvatar;
|
||||
this.count = count;
|
||||
this.optionId = optionId;
|
||||
this.isEnterMpDungeonTeam = isEnterMpDungeonTeam;
|
||||
}
|
||||
|
||||
public UseItemParams(Player player, ItemUseTarget itemUseTarget) {
|
||||
this.player = player;
|
||||
this.itemUseTarget = itemUseTarget;
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package emu.grasscutter.game.shop;
|
||||
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ShopChestTable {
|
||||
private int itemId;
|
||||
private List<ItemParamData> containsItem = new ArrayList<>();
|
||||
|
||||
public int getItemId() {
|
||||
return itemId;
|
||||
}
|
||||
|
||||
public void setItemId(int itemId) {
|
||||
this.itemId = itemId;
|
||||
}
|
||||
|
||||
public List<ItemParamData> getContainsItem() {
|
||||
return containsItem;
|
||||
}
|
||||
|
||||
public void setContainsItem(List<ItemParamData> containsItem) {
|
||||
this.containsItem = containsItem;
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package emu.grasscutter.game.shop;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.DataLoader;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.excels.ShopGoodsData;
|
||||
import emu.grasscutter.server.game.BaseGameSystem;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
@ -14,10 +15,11 @@ import static emu.grasscutter.config.Configuration.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ShopSystem extends BaseGameSystem {
|
||||
private final Int2ObjectMap<List<ShopInfo>> shopData;
|
||||
private final List<ShopChestTable> shopChestData;
|
||||
private final Int2ObjectMap<List<ItemParamData>> shopChestData;
|
||||
|
||||
private static final int REFRESH_HOUR = 4; // In GMT+8 server
|
||||
private static final String TIME_ZONE = "Asia/Shanghai"; // GMT+8 Timezone
|
||||
@ -25,7 +27,7 @@ public class ShopSystem extends BaseGameSystem {
|
||||
public ShopSystem(GameServer server) {
|
||||
super(server);
|
||||
this.shopData = new Int2ObjectOpenHashMap<>();
|
||||
this.shopChestData = new ArrayList<>();
|
||||
this.shopChestData = new Int2ObjectOpenHashMap<>();
|
||||
this.load();
|
||||
}
|
||||
|
||||
@ -33,8 +35,8 @@ public class ShopSystem extends BaseGameSystem {
|
||||
return shopData;
|
||||
}
|
||||
|
||||
public List<ShopChestTable> getShopChestData() {
|
||||
return shopChestData;
|
||||
public List<ItemParamData> getShopChestData(int chestId) {
|
||||
return this.shopChestData.get(chestId);
|
||||
}
|
||||
|
||||
public static int getShopNextRefreshTime(ShopInfo shopInfo) {
|
||||
@ -76,15 +78,22 @@ public class ShopSystem extends BaseGameSystem {
|
||||
}
|
||||
|
||||
private void loadShopChest() {
|
||||
getShopChestData().clear();
|
||||
shopChestData.clear();
|
||||
try {
|
||||
List<ShopChestTable> shopChestTableList = DataLoader.loadList("ShopChest.json", ShopChestTable.class);
|
||||
if (shopChestTableList.size() > 0) {
|
||||
getShopChestData().addAll(shopChestTableList);
|
||||
Grasscutter.getLogger().debug("ShopChest data successfully loaded.");
|
||||
} else {
|
||||
Grasscutter.getLogger().error("Unable to load ShopChest data. ShopChest data size is 0.");
|
||||
}
|
||||
Map<Integer, String> chestMap = DataLoader.loadMap("ShopChest.v2.json", Integer.class, String.class);
|
||||
chestMap.forEach((chestId, itemStr) -> {
|
||||
if (itemStr.isEmpty()) return;
|
||||
var entries = itemStr.split(",");
|
||||
var list = new ArrayList<ItemParamData>(entries.length);
|
||||
for (var entry : entries) {
|
||||
var idAndCount = entry.split(":");
|
||||
int id = Integer.parseInt(idAndCount[0]);
|
||||
int count = Integer.parseInt(idAndCount[1]);
|
||||
list.add(new ItemParamData(id, count));
|
||||
}
|
||||
this.shopChestData.put((int) chestId, list);
|
||||
});
|
||||
Grasscutter.getLogger().debug("Loaded " + chestMap.size() + " ShopChest entries.");
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Unable to load ShopChest data.", e);
|
||||
}
|
||||
|
@ -2,26 +2,33 @@ package emu.grasscutter.game.systems;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.common.ItemUseData;
|
||||
import emu.grasscutter.data.excels.AvatarPromoteData;
|
||||
import emu.grasscutter.data.excels.AvatarSkillDepotData;
|
||||
import emu.grasscutter.data.excels.ItemData;
|
||||
import emu.grasscutter.data.excels.WeaponPromoteData;
|
||||
import emu.grasscutter.data.excels.AvatarSkillDepotData.InherentProudSkillOpens;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.inventory.ItemType;
|
||||
import emu.grasscutter.game.inventory.MaterialType;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
import emu.grasscutter.game.props.ItemUseTarget;
|
||||
import emu.grasscutter.game.shop.ShopChestTable;
|
||||
import emu.grasscutter.game.props.ItemUseAction.ItemUseAddExp;
|
||||
import emu.grasscutter.game.props.ItemUseAction.ItemUseAddReliquaryExp;
|
||||
import emu.grasscutter.game.props.ItemUseAction.ItemUseAddWeaponExp;
|
||||
import emu.grasscutter.game.props.ItemUseAction.UseItemParams;
|
||||
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
|
||||
import emu.grasscutter.net.proto.MaterialInfoOuterClass.MaterialInfo;
|
||||
import emu.grasscutter.server.event.player.PlayerUseFoodEvent;
|
||||
@ -29,28 +36,32 @@ import emu.grasscutter.server.game.BaseGameSystem;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntRBTreeMap;
|
||||
import lombok.val;
|
||||
|
||||
public class InventorySystem extends BaseGameSystem {
|
||||
private final static int RELIC_MATERIAL_1 = 105002; // Sanctifying Unction
|
||||
private final static int RELIC_MATERIAL_2 = 105003; // Sanctifying Essence
|
||||
private final static int RELIC_MATERIAL_EXP_1 = 2500; // Sanctifying Unction
|
||||
private final static int RELIC_MATERIAL_EXP_2 = 10000; // Sanctifying Essence
|
||||
|
||||
private final static int WEAPON_ORE_1 = 104011; // Enhancement Ore
|
||||
private final static int WEAPON_ORE_2 = 104012; // Fine Enhancement Ore
|
||||
private final static int WEAPON_ORE_3 = 104013; // Mystic Enhancement Ore
|
||||
private final static int WEAPON_ORE_EXP_1 = 400; // Enhancement Ore
|
||||
private final static int WEAPON_ORE_EXP_2 = 2000; // Fine Enhancement Ore
|
||||
private final static int WEAPON_ORE_EXP_3 = 10000; // Mystic Enhancement Ore
|
||||
|
||||
private final static int AVATAR_BOOK_1 = 104001; // Wanderer's Advice
|
||||
private final static int AVATAR_BOOK_2 = 104002; // Adventurer's Experience
|
||||
private final static int AVATAR_BOOK_3 = 104003; // Hero's Wit
|
||||
private final static int AVATAR_BOOK_EXP_1 = 1000; // Wanderer's Advice
|
||||
private final static int AVATAR_BOOK_EXP_2 = 5000; // Adventurer's Experience
|
||||
private final static int AVATAR_BOOK_EXP_3 = 20000; // Hero's Wit
|
||||
private static final Int2IntMap weaponRefundMaterials = new Int2IntArrayMap();
|
||||
{
|
||||
// Use a sorted map, use exp as key to sort by exp
|
||||
// We want to have weaponRefundMaterials as (id, exp) in descending exp order
|
||||
var temp = new Int2IntRBTreeMap(Collections.reverseOrder());
|
||||
GameData.getItemDataMap().forEach((id, data) -> {
|
||||
if (data == null) return;
|
||||
if (data.getMaterialType() != MaterialType.MATERIAL_WEAPON_EXP_STONE) return;
|
||||
var actions = data.getItemUseActions();
|
||||
if (actions == null) return;
|
||||
for (var action : actions) {
|
||||
if (action.getItemUseOp() == ItemUseOp.ITEM_USE_ADD_WEAPON_EXP) {
|
||||
temp.putIfAbsent((int) ((ItemUseAddWeaponExp) action).getExp(), (int) id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
temp.forEach((exp, id) -> weaponRefundMaterials.putIfAbsent((int) id, (int) exp));
|
||||
}
|
||||
|
||||
public InventorySystem(GameServer server) {
|
||||
super(server);
|
||||
@ -88,8 +99,9 @@ public class InventorySystem extends BaseGameSystem {
|
||||
continue;
|
||||
}
|
||||
// Calculate mora cost
|
||||
moraCost += food.getItemData().getBaseConvExp();
|
||||
expGain += food.getItemData().getBaseConvExp();
|
||||
int exp = food.getItemData().getBaseConvExp();
|
||||
moraCost += exp;
|
||||
expGain += exp;
|
||||
// Feeding artifact with exp already
|
||||
if (food.getTotalExp() > 0) {
|
||||
expGain += (food.getTotalExp() * 4) / 5;
|
||||
@ -99,11 +111,19 @@ public class InventorySystem extends BaseGameSystem {
|
||||
List<ItemParamData> payList = new ArrayList<ItemParamData>();
|
||||
for (ItemParam itemParam : list) {
|
||||
int amount = itemParam.getCount(); // Previously this capped to inventory amount, but rejecting the payment makes more sense for an invalid order
|
||||
int gain = amount * switch (itemParam.getItemId()) {
|
||||
case RELIC_MATERIAL_1 -> RELIC_MATERIAL_EXP_1;
|
||||
case RELIC_MATERIAL_2 -> RELIC_MATERIAL_EXP_2;
|
||||
default -> 0;
|
||||
};
|
||||
int gain = 0;
|
||||
var data = GameData.getItemDataMap().get(itemParam.getItemId());
|
||||
if (data != null) {
|
||||
var actions = data.getItemUseActions();
|
||||
if (actions != null) {
|
||||
for (var action : actions) {
|
||||
if (action.getItemUseOp() == ItemUseOp.ITEM_USE_ADD_RELIQUARY_EXP) {
|
||||
gain += ((ItemUseAddReliquaryExp) action).getExp();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
gain *= amount;
|
||||
expGain += gain;
|
||||
moraCost += gain;
|
||||
payList.add(new ItemParamData(itemParam.getItemId(), itemParam.getCount()));
|
||||
@ -197,25 +217,39 @@ public class InventorySystem extends BaseGameSystem {
|
||||
}
|
||||
|
||||
// Get exp gain
|
||||
int expGain = 0;
|
||||
for (long guid : foodWeaponGuidList) {
|
||||
GameItem food = player.getInventory().getItemByGuid(guid);
|
||||
if (food == null) {
|
||||
continue;
|
||||
}
|
||||
expGain += food.getItemData().getWeaponBaseExp();
|
||||
if (food.getTotalExp() > 0) {
|
||||
expGain += (food.getTotalExp() * 4) / 5;
|
||||
}
|
||||
}
|
||||
for (ItemParam param : itemParamList) {
|
||||
expGain += param.getCount() * switch (param.getItemId()) {
|
||||
case WEAPON_ORE_1 -> WEAPON_ORE_EXP_1;
|
||||
case WEAPON_ORE_2 -> WEAPON_ORE_EXP_2;
|
||||
case WEAPON_ORE_3 -> WEAPON_ORE_EXP_3;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
int expGain = foodWeaponGuidList.stream()
|
||||
.map(player.getInventory()::getItemByGuid)
|
||||
.filter(Objects::nonNull)
|
||||
.mapToInt(food -> food.getItemData().getWeaponBaseExp() + ((food.getTotalExp() * 4) / 5))
|
||||
.sum();
|
||||
// Stream::ofNullable version
|
||||
expGain += itemParamList.stream()
|
||||
.mapToInt(param -> {
|
||||
int exp = Stream.ofNullable(GameData.getItemDataMap().get(param.getItemId()))
|
||||
.map(ItemData::getItemUseActions)
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(Collection::stream)
|
||||
.filter(action -> action.getItemUseOp() == ItemUseOp.ITEM_USE_ADD_WEAPON_EXP)
|
||||
.mapToInt(action -> ((ItemUseAddWeaponExp) action).getExp())
|
||||
.sum();
|
||||
return exp * param.getCount();
|
||||
})
|
||||
.sum();
|
||||
// Optional::ofNullable version
|
||||
// expGain += itemParamList.stream()
|
||||
// .mapToInt(param -> {
|
||||
// int exp = Optional.ofNullable(GameData.getItemDataMap().get(param.getItemId()))
|
||||
// .map(ItemData::getItemUseActions)
|
||||
// .map(actions -> {
|
||||
// return actions.stream()
|
||||
// .filter(action -> action.getItemUseOp() == ItemUseOp.ITEM_USE_ADD_WEAPON_EXP)
|
||||
// .mapToInt(action -> ((ItemUseAddWeaponExp) action).getExp())
|
||||
// .sum();
|
||||
// })
|
||||
// .orElse(0);
|
||||
// return exp * param.getCount();
|
||||
// })
|
||||
// .sum();
|
||||
|
||||
// Try
|
||||
int maxLevel = promoteData.getUnlockMaxLevel();
|
||||
@ -272,13 +306,19 @@ public class InventorySystem extends BaseGameSystem {
|
||||
List<ItemParamData> payList = new ArrayList<ItemParamData>();
|
||||
for (ItemParam param : itemParamList) {
|
||||
int amount = param.getCount(); // Previously this capped to inventory amount, but rejecting the payment makes more sense for an invalid order
|
||||
int gain = amount * switch (param.getItemId()) {
|
||||
case WEAPON_ORE_1 -> WEAPON_ORE_EXP_1;
|
||||
case WEAPON_ORE_2 -> WEAPON_ORE_EXP_2;
|
||||
case WEAPON_ORE_3 -> WEAPON_ORE_EXP_3;
|
||||
default -> 0;
|
||||
};
|
||||
expGain += gain;
|
||||
|
||||
var data = GameData.getItemDataMap().get(param.getItemId());
|
||||
if (data != null) {
|
||||
var actions = data.getItemUseActions();
|
||||
if (actions != null) {
|
||||
for (var action : actions) {
|
||||
if (action.getItemUseOp() == ItemUseOp.ITEM_USE_ADD_WEAPON_EXP) {
|
||||
expGain += ((ItemUseAddWeaponExp) action).getExp() * amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
payList.add(new ItemParamData(param.getItemId(), amount));
|
||||
}
|
||||
|
||||
@ -344,23 +384,13 @@ public class InventorySystem extends BaseGameSystem {
|
||||
private List<ItemParam> getLeftoverOres(int leftover) {
|
||||
List<ItemParam> leftoverOreList = new ArrayList<>(3);
|
||||
|
||||
if (leftover < WEAPON_ORE_EXP_1) {
|
||||
return leftoverOreList;
|
||||
}
|
||||
for (var e : weaponRefundMaterials.int2IntEntrySet()) {
|
||||
int exp = e.getIntValue();
|
||||
int ores = leftover / exp;
|
||||
leftover = leftover % exp;
|
||||
|
||||
// Get leftovers
|
||||
int ore3 = leftover / WEAPON_ORE_EXP_3;
|
||||
leftover = leftover % WEAPON_ORE_EXP_3;
|
||||
int ore2 = leftover / WEAPON_ORE_EXP_2;
|
||||
leftover = leftover % WEAPON_ORE_EXP_2;
|
||||
int ore1 = leftover / WEAPON_ORE_EXP_1;
|
||||
|
||||
if (ore3 > 0) {
|
||||
leftoverOreList.add(ItemParam.newBuilder().setItemId(WEAPON_ORE_3).setCount(ore3).build());
|
||||
} if (ore2 > 0) {
|
||||
leftoverOreList.add(ItemParam.newBuilder().setItemId(WEAPON_ORE_2).setCount(ore2).build());
|
||||
} if (ore1 > 0) {
|
||||
leftoverOreList.add(ItemParam.newBuilder().setItemId(WEAPON_ORE_1).setCount(ore1).build());
|
||||
if (ores > 0)
|
||||
leftoverOreList.add(ItemParam.newBuilder().setItemId(e.getIntKey()).setCount(ores).build());
|
||||
}
|
||||
|
||||
return leftoverOreList;
|
||||
@ -503,22 +533,18 @@ public class InventorySystem extends BaseGameSystem {
|
||||
avatar.setPromoteLevel(nextPromoteLevel);
|
||||
|
||||
// Update proud skills
|
||||
AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(avatar.getSkillDepotId());
|
||||
|
||||
if (skillDepot != null && skillDepot.getInherentProudSkillOpens() != null) {
|
||||
for (InherentProudSkillOpens openData : skillDepot.getInherentProudSkillOpens()) {
|
||||
if (openData.getProudSkillGroupId() == 0) {
|
||||
continue;
|
||||
}
|
||||
if (openData.getNeedAvatarPromoteLevel() == avatar.getPromoteLevel()) {
|
||||
int proudSkillId = (openData.getProudSkillGroupId() * 100) + 1;
|
||||
if (GameData.getProudSkillDataMap().containsKey(proudSkillId)) {
|
||||
avatar.getProudSkillList().add(proudSkillId);
|
||||
player.sendPacket(new PacketProudSkillChangeNotify(avatar));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Optional.ofNullable(GameData.getAvatarSkillDepotDataMap().get(avatar.getSkillDepotId()))
|
||||
.map(AvatarSkillDepotData::getInherentProudSkillOpens)
|
||||
.ifPresent(d -> d.stream()
|
||||
.filter(openData -> openData.getProudSkillGroupId() > 0)
|
||||
.filter(openData -> openData.getNeedAvatarPromoteLevel() == avatar.getPromoteLevel())
|
||||
.mapToInt(openData -> (openData.getProudSkillGroupId() * 100) + 1)
|
||||
.filter(GameData.getProudSkillDataMap()::containsKey)
|
||||
.forEach(proudSkillId -> {
|
||||
avatar.getProudSkillList().add(proudSkillId);
|
||||
player.sendPacket(new PacketProudSkillChangeNotify(avatar));
|
||||
})
|
||||
);
|
||||
|
||||
// Packets
|
||||
player.sendPacket(new PacketAvatarPropNotify(avatar));
|
||||
@ -543,12 +569,19 @@ public class InventorySystem extends BaseGameSystem {
|
||||
}
|
||||
|
||||
// Calc exp
|
||||
int expGain = switch (itemId) {
|
||||
case AVATAR_BOOK_1 -> AVATAR_BOOK_EXP_1 * count;
|
||||
case AVATAR_BOOK_2 -> AVATAR_BOOK_EXP_2 * count;
|
||||
case AVATAR_BOOK_3 -> AVATAR_BOOK_EXP_3 * count;
|
||||
default -> 0;
|
||||
};
|
||||
int expGain = 0;
|
||||
|
||||
var data = GameData.getItemDataMap().get(itemId);
|
||||
if (data != null) {
|
||||
var actions = data.getItemUseActions();
|
||||
if (actions != null) {
|
||||
for (var action : actions) {
|
||||
if (action.getItemUseOp() == ItemUseOp.ITEM_USE_ADD_EXP) {
|
||||
expGain += ((ItemUseAddExp) action).getExp() * count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (expGain <= 0) {
|
||||
@ -699,187 +732,85 @@ public class InventorySystem extends BaseGameSystem {
|
||||
player.sendPacket(new PacketDestroyMaterialRsp(returnMaterialMap));
|
||||
}
|
||||
|
||||
public GameItem useItem(Player player, long targetGuid, long itemGuid, int count, int optionId) {
|
||||
// Uses an item from the player's inventory.
|
||||
public synchronized GameItem useItem(Player player, long targetGuid, long itemGuid, int count, int optionId, boolean isEnterMpDungeonTeam) {
|
||||
Grasscutter.getLogger().info("Attempting to use item from inventory");
|
||||
Avatar target = player.getAvatars().getAvatarByGuid(targetGuid);
|
||||
GameItem useItem = player.getInventory().getItemByGuid(itemGuid);
|
||||
|
||||
if (useItem == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int used = 0;
|
||||
boolean useSuccess = false;
|
||||
ItemData itemData = useItem.getItemData();
|
||||
GameItem item = player.getInventory().getItemByGuid(itemGuid);
|
||||
if (item == null) return null;
|
||||
if (item.getCount() < count) return null;
|
||||
ItemData itemData = item.getItemData();
|
||||
if (itemData == null) return null;
|
||||
|
||||
// Use
|
||||
switch (itemData.getMaterialType()) {
|
||||
case MATERIAL_FOOD:
|
||||
if (itemData.getUseTarget() == ItemUseTarget.ITEM_USE_TARGET_SPECIFY_DEAD_AVATAR) {
|
||||
if (target == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Invoke player use food event.
|
||||
PlayerUseFoodEvent event = new PlayerUseFoodEvent(player, itemData, target.getAsEntity());
|
||||
// Call the event.
|
||||
event.call(); if (!event.isCanceled()) {
|
||||
used = player.getTeamManager().reviveAvatar(target) ? 1 : 0;
|
||||
}
|
||||
} else {
|
||||
used = 1;
|
||||
}
|
||||
break;
|
||||
case MATERIAL_NOTICE_ADD_HP:
|
||||
if (itemData.getUseTarget() == ItemUseTarget.ITEM_USE_TARGET_SPECIFY_ALIVE_AVATAR) {
|
||||
if (target == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Invoke player use food event.
|
||||
PlayerUseFoodEvent event = new PlayerUseFoodEvent(player, itemData, target.getAsEntity());
|
||||
// Call the event.
|
||||
event.call(); if (!event.isCanceled()) {
|
||||
int[] SatiationParams = itemData.getSatiationParams();
|
||||
used = player.getTeamManager().healAvatar(target, SatiationParams[0], SatiationParams[1]) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MATERIAL_CONSUME:
|
||||
// Make sure we have usage data for this material.
|
||||
if (itemData.getItemUse() == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
ItemUseOp useOp = itemData.getItemUse().get(0).getUseOp();
|
||||
|
||||
// Unlock item based on use operation
|
||||
useSuccess = switch (useOp) {
|
||||
case ITEM_USE_UNLOCK_FORGE -> player.getForgingManager().unlockForgingBlueprint(useItem);
|
||||
case ITEM_USE_UNLOCK_COMBINE -> player.getServer().getCombineSystem().unlockCombineDiagram(player, useItem);
|
||||
case ITEM_USE_UNLOCK_COOK_RECIPE -> player.getCookingManager().unlockRecipe(useItem);
|
||||
default -> useSuccess;
|
||||
};
|
||||
break;
|
||||
case MATERIAL_FURNITURE_FORMULA:
|
||||
case MATERIAL_FURNITURE_SUITE_FORMULA:
|
||||
if (itemData.getItemUse() == null) {
|
||||
break;
|
||||
}
|
||||
useSuccess = player.getFurnitureManager().unlockFurnitureOrSuite(useItem);
|
||||
|
||||
break;
|
||||
case MATERIAL_CONSUME_BATCH_USE:
|
||||
// Make sure we have usage data for this material.
|
||||
if (itemData.getItemUse() == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle fragile/transient resin.
|
||||
if (useItem.getItemId() == 107009 || useItem.getItemId() == 107012) {
|
||||
// Add resin to the inventory.
|
||||
ItemData resinItemData = GameData.getItemDataMap().get(106);
|
||||
player.getInventory().addItem(new GameItem(resinItemData, 60 * count), ActionReason.PlayerUseItem);
|
||||
|
||||
// Set used amount.
|
||||
used = count;
|
||||
}
|
||||
break;
|
||||
case MATERIAL_CHEST:
|
||||
List<ShopChestTable> shopChestTableList = player.getServer().getShopSystem().getShopChestData();
|
||||
List<GameItem> rewardItemList = new ArrayList<>();
|
||||
for (ShopChestTable shopChestTable : shopChestTableList) {
|
||||
if (shopChestTable.getItemId() != useItem.getItemId()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (shopChestTable.getContainsItem() == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (ItemParamData itemParamData : shopChestTable.getContainsItem()) {
|
||||
ItemData containedItem = GameData.getItemDataMap().get(itemParamData.getId());
|
||||
if (containedItem == null) {
|
||||
continue;
|
||||
}
|
||||
rewardItemList.add(new GameItem(containedItem, itemParamData.getCount()));
|
||||
}
|
||||
|
||||
if (!rewardItemList.isEmpty()) {
|
||||
player.getInventory().addItems(rewardItemList, ActionReason.Shop);
|
||||
}
|
||||
|
||||
used = 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case MATERIAL_CHEST_BATCH_USE:
|
||||
if (optionId < 1) return null; // 1-indexed selection
|
||||
for (var use : itemData.getItemUse()) {
|
||||
if (use.getUseOp() != ItemUseOp.ITEM_USE_CHEST_SELECT_ITEM) continue;
|
||||
String[] choices = use.getUseParam()[0].split(",");
|
||||
if (optionId > choices.length) return null;
|
||||
String[] choiceParts = choices[optionId-1].split(":");
|
||||
int optionItemId = Integer.parseInt(choiceParts[0]);
|
||||
int optionItemCount = Integer.parseInt(choiceParts[1]);
|
||||
ItemData optionItem = GameData.getItemDataMap().get(optionItemId);
|
||||
if (optionItem == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
player.getInventory().addItem(new GameItem(optionItem, optionItemCount * count), ActionReason.Shop);
|
||||
|
||||
used = count;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case MATERIAL_BGM:
|
||||
ItemUseData use = itemData.getItemUse().get(0);
|
||||
if (use.getUseOp() == ItemUseOp.ITEM_USE_UNLOCK_HOME_BGM) {
|
||||
int bgmId = Integer.parseInt(use.getUseParam()[0]);
|
||||
player.getInventory().removeItem(useItem, 1);
|
||||
player.sendPacket(new PacketUnlockHomeBgmNotify(bgmId));
|
||||
player.getHome().addUnlockedHomeBgm(bgmId);
|
||||
player.sendPacket(new PacketUnlockedHomeBgmNotify(player));
|
||||
return useItem;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
var params = new UseItemParams(player, itemData.getUseTarget(), target, count, optionId, isEnterMpDungeonTeam);
|
||||
if (useItemDirect(itemData, params)) {
|
||||
player.getInventory().removeItem(item, count);
|
||||
var actions = itemData.getItemUseActions();
|
||||
if (actions != null)
|
||||
actions.forEach(use -> use.postUseItem(params));
|
||||
Grasscutter.getLogger().info("Item use succeeded!");
|
||||
return item;
|
||||
} else {
|
||||
Grasscutter.getLogger().info("Item use failed!");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Welkin
|
||||
if (useItem.getItemId() == 1202) {
|
||||
player.rechargeMoonCard();
|
||||
used = 1;
|
||||
}
|
||||
// Uses an item without checking the player's inventory.
|
||||
public synchronized boolean useItemDirect(ItemData itemData, UseItemParams params) {
|
||||
if (itemData == null) return false;
|
||||
|
||||
// If we used at least one item, or one of the methods called here reports using the item successfully,
|
||||
// we return the item to make UseItemRsp a success.
|
||||
if (used > 0) {
|
||||
// Handle use params, mainly server buffs
|
||||
for (ItemUseData useData : itemData.getItemUse()) {
|
||||
switch (useData.getUseOp()) {
|
||||
case ITEM_USE_ADD_SERVER_BUFF -> {
|
||||
int buffId = Integer.parseInt(useData.getUseParam()[0]);
|
||||
String timeString = useData.getUseParam()[1];
|
||||
float time = timeString.isEmpty() ? 0 : Float.parseFloat(timeString);
|
||||
|
||||
player.getBuffManager().addBuff(buffId, time);
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
// Ensure targeting conditions are satisfied
|
||||
val target = Optional.ofNullable(params.targetAvatar);
|
||||
switch (params.itemUseTarget) {
|
||||
case ITEM_USE_TARGET_NONE -> {}
|
||||
case ITEM_USE_TARGET_SPECIFY_AVATAR -> {
|
||||
if (target.isEmpty()) return false;
|
||||
}
|
||||
|
||||
// Remove item from inventory since we used it
|
||||
player.getInventory().removeItem(useItem, used);
|
||||
return useItem;
|
||||
case ITEM_USE_TARGET_SPECIFY_ALIVE_AVATAR -> {
|
||||
if (target.map(a -> !a.getAsEntity().isAlive()).orElse(true)) return false;
|
||||
}
|
||||
case ITEM_USE_TARGET_SPECIFY_DEAD_AVATAR -> {
|
||||
if (target.map(a -> a.getAsEntity().isAlive()).orElse(true)) return false;
|
||||
}
|
||||
case ITEM_USE_TARGET_CUR_AVATAR -> {}
|
||||
case ITEM_USE_TARGET_CUR_TEAM -> {}
|
||||
}
|
||||
|
||||
if (useSuccess) {
|
||||
return useItem;
|
||||
int[] satiationParams = itemData.getSatiationParams();
|
||||
if (satiationParams != null && target.isPresent()) {
|
||||
// Invoke and call player use food event.
|
||||
var event = new PlayerUseFoodEvent(params.player, itemData, params.targetAvatar.getAsEntity()); event.call();
|
||||
if (event.isCanceled()) return false;
|
||||
|
||||
float satiationIncrease = satiationParams[0] + ((float)satiationParams[1])/params.targetAvatar.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||
if (!params.targetAvatar.addSatiation(satiationIncrease)) { // Make sure avatar can eat
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
// Use
|
||||
var actions = itemData.getItemUseActions();
|
||||
Grasscutter.getLogger().info("Using - actions - {}", actions);
|
||||
if (actions == null) return true; // Maybe returning false would be more appropriate?
|
||||
return actions.stream()
|
||||
.map(use -> use.useItem(params))
|
||||
.reduce(false, (a,b) -> a || b); // Don't short-circuit!!!
|
||||
}
|
||||
|
||||
public static synchronized int checkPlayerAvatarConstellationLevel(Player player, int itemId) {
|
||||
ItemData itemData = GameData.getItemDataMap().get(itemId);
|
||||
if ((itemData == null) || (itemData.getMaterialType() != MaterialType.MATERIAL_AVATAR)) {
|
||||
return -2; // Not an Avatar
|
||||
}
|
||||
Avatar avatar = player.getAvatars().getAvatarById((itemId % 1000) + 10000000);
|
||||
if (avatar == null) {
|
||||
return -1; // Doesn't have
|
||||
}
|
||||
// Constellation
|
||||
int constLevel = avatar.getCoreProudSkillLevel();
|
||||
GameItem constItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId + 100);
|
||||
constLevel += Optional.ofNullable(constItem).map(GameItem::getCount).orElse(0);
|
||||
return constLevel;
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import emu.grasscutter.net.proto.Unk2700BEDLIGJANCJClientReq;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
import emu.grasscutter.server.packet.send.PacketChangeHomeBgmNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketChangeHomeBgmRsp;
|
||||
import emu.grasscutter.server.packet.send.PacketUnlockedHomeBgmNotify;
|
||||
|
||||
@Opcodes(PacketOpcodes.Unk2700_BEDLIGJANCJ_ClientReq)
|
||||
public class HandlerChangeHomeBgmReq extends PacketHandler {
|
||||
@ -17,11 +16,8 @@ public class HandlerChangeHomeBgmReq extends PacketHandler {
|
||||
|
||||
int homeBgmId = req.getUnk2700BJHAMKKECEI();
|
||||
var home = session.getPlayer().getHome();
|
||||
if (!home.getUnlockedHomeBgmListInfo().contains(homeBgmId)) {
|
||||
home.addUnlockedHomeBgm(homeBgmId);
|
||||
session.send(new PacketUnlockedHomeBgmNotify(session.getPlayer()));
|
||||
}
|
||||
|
||||
home.addUnlockedHomeBgm(homeBgmId); // Not sure if this is sane
|
||||
home.getHomeSceneItem(session.getPlayer().getSceneId()).setHomeBgmId(homeBgmId);
|
||||
home.save();
|
||||
|
||||
|
@ -15,7 +15,7 @@ public class HandlerUseItemReq extends PacketHandler {
|
||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||
UseItemReq req = UseItemReq.parseFrom(payload);
|
||||
|
||||
GameItem useItem = session.getServer().getInventorySystem().useItem(session.getPlayer(), req.getTargetGuid(), req.getGuid(), req.getCount(), req.getOptionIdx());
|
||||
GameItem useItem = session.getServer().getInventorySystem().useItem(session.getPlayer(), req.getTargetGuid(), req.getGuid(), req.getCount(), req.getOptionIdx(), req.getIsEnterMpDungeonTeam());
|
||||
if (useItem != null) {
|
||||
session.send(new PacketUseItemRsp(req.getTargetGuid(), useItem));
|
||||
} else {
|
||||
|
@ -1,153 +0,0 @@
|
||||
[
|
||||
{
|
||||
"itemId": 115019,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 104002,
|
||||
"Count": 40
|
||||
},
|
||||
{
|
||||
"Id": 202,
|
||||
"Count": 30000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115020,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 104013,
|
||||
"Count": 25
|
||||
},
|
||||
{
|
||||
"Id": 202,
|
||||
"Count": 30000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115021,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 115013,
|
||||
"Count": 5
|
||||
},
|
||||
{
|
||||
"Id": 104003,
|
||||
"Count": 40
|
||||
},
|
||||
{
|
||||
"Id": 202,
|
||||
"Count": 120000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115022,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 115017,
|
||||
"Count": 25
|
||||
},
|
||||
{
|
||||
"Id": 202,
|
||||
"Count": 150000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115023,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 115025,
|
||||
"Count": 10
|
||||
},
|
||||
{
|
||||
"Id": 202,
|
||||
"Count": 60000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115029,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 104013,
|
||||
"Count": 100
|
||||
},
|
||||
{
|
||||
"Id": 202,
|
||||
"Count": 100000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115030,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 104003,
|
||||
"Count": 12
|
||||
},
|
||||
{
|
||||
"Id": 202,
|
||||
"Count": 10000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115034,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 115013,
|
||||
"Count": 6
|
||||
},
|
||||
{
|
||||
"Id": 202,
|
||||
"Count": 60000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115032,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 115024,
|
||||
"Count": 12
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115010,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 104002,
|
||||
"Count": 80
|
||||
},
|
||||
{
|
||||
"Id": 104012,
|
||||
"Count": 40
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115011,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 104003,
|
||||
"Count": 50
|
||||
},
|
||||
{
|
||||
"Id": 104013,
|
||||
"Count": 25
|
||||
},
|
||||
{
|
||||
"Id": 107009,
|
||||
"Count": 1
|
||||
},
|
||||
{
|
||||
"Id": 202,
|
||||
"Count": 50000
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
29
src/main/resources/defaults/data/ShopChest.v2.json
Normal file
29
src/main/resources/defaults/data/ShopChest.v2.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"212000002":"",
|
||||
"1000100":"",
|
||||
"7000600":"",
|
||||
"7000700":"",
|
||||
"7000800":"",
|
||||
"7000900":"",
|
||||
"7001000":"",
|
||||
"7001100":"",
|
||||
"7001200":"",
|
||||
"7001300":"",
|
||||
"7001400":"",
|
||||
|
||||
"23010000": "104002:80,104012:40",
|
||||
"23010001": "104003:50,104013:25,107009:1,202:50000",
|
||||
"23010100": "104002:40,202:30000",
|
||||
"23010101": "104013:25,202:30000",
|
||||
"23010102": "115013:5,104003:40,202:120000",
|
||||
"23010103": "115017:25,202:150000",
|
||||
"23010104": "115025:10,202:60000",
|
||||
"23010105": "104013:100,202:100000",
|
||||
"23010106": "104003:12,202:10000",
|
||||
"23010107": "115024:12",
|
||||
"23010109": "115013:6,202:60000",
|
||||
"23020000": "109449:1,109450:1",
|
||||
"23020001": "373345:1",
|
||||
"23020002": "373341:1",
|
||||
"23020003": "373342:1"
|
||||
}
|
Loading…
Reference in New Issue
Block a user