using MetaAssets; using Microsoft.IdentityModel.Tokens; using ServerCommon; using ServerCore; using ServerBase; namespace GameServer { public class AbilityAction : EntityActionBase { private Dictionary m_abilities { get; set; } = new(); public AbilityAction(EntityBase owner) : base(owner) { } public override async Task onInit() { await Task.CompletedTask; var result = new Result(); clearAbility(); return result; } public override void onClear() { return; } public void clearAbility() { foreach (var ability in EnumHelper.getValuesWithoutScope()) { m_abilities[ability] = 0; } } public int? getAbility(AttributeType abilityId) => m_abilities.TryGetValue(abilityId, out var ability) ? ability : null; public Dictionary getAbilities() => m_abilities; public async Task forceResetAll() { clearAbility(); var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!! - {toBasicString()}"); var inventory_action_base = owner.getEntityAction(); NullReferenceCheckHelper.throwIfNull(inventory_action_base, () => $"inventory_action_base is null !!! - {owner.toBasicString()}"); var result = await inventory_action_base.tryResetAttributeByTattoo(); if(result.isFail()) { return result; } var buff_action = owner.getEntityAction(); NullReferenceCheckHelper.throwIfNull(buff_action, () => $"buff_action is null !!! - {owner.toBasicString()}"); result = await buff_action.tryResetAttributeByBuff(); if (result.isFail()) { return result; } return result; } public void forceSetAbilityAll(int toSetAbility) { var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!! - {toBasicString()}"); var attribute_types = EnumHelper.getValuesWithoutScopeAll(); foreach (var attribute_type in attribute_types) { m_abilities[attribute_type] = toSetAbility; } } public void setAbility(AttributeType abilityId, int ability, bool decrease) { var change_value = decrease ? ability * -1 : ability; m_abilities[abilityId] += change_value; } public async Task setAbilities(ItemBase item, Int16 level, bool decrease) { ArgumentNullReferenceCheckHelper.throwIfNull(item, () => $"item is null !!! - {toBasicString()}"); await Task.CompletedTask; var result = new Result(); string err_msg; var item_attribute = item.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(item_attribute, () => $"item_attribute is null !!! - {toBasicString()}"); var item_meta = item.getItemMeta(); NullReferenceCheckHelper.throwIfNull(item_meta, () => $"item_meta is null !!! - {toBasicString()}"); var enchant = getEnchantData(level, item_meta); if (null == enchant) { err_msg = $"Fail to get enchant data : {nameof(getEnchantData)}"; result.setFail(ServerErrorCode.ItemEnchantNotFoundInMeta, err_msg); Log.getLogger().error(err_msg); return result; } // 배열의 위치를 AttributeType으로 활용하다보니 불필요한 예외 처리 코드를 작성해야 한다. !!! // Key-Value 구조로 바꾸고 싶지만... 간단한 수정 범위는 아니다. // MetaHelper를 통해 메타 데이터를 가공하는 방법도 있다.... - kangms for(var i = 0; i < enchant.Count; i++) { if (false == EnumHelper.isDefined(item_attribute.Attributes[i])) { Log.getLogger().warn($"Invalid Ability Enum Type : {item_attribute.Attributes[i]}"); continue; } if (item_attribute.Attributes.Count <= i) { Log.getLogger().warn($"Ability Type outof range !!! : enchantIndex:{i} > itemAttributeIndex:{i}"); item_attribute.Attributes.Add(0); } setAbility((AttributeType)item_attribute.Attributes[i], enchant[i], decrease); } return result; } public async Task changeAbilities(ItemBase item, Dictionary slotWithAttributeIds, bool decrease) { await Task.CompletedTask; var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); ArgumentNullReferenceCheckHelper.throwIfNull(item, () => $"item is null !!! - {owner.toBasicString()}"); ArgumentNullReferenceCheckHelper.throwIfNull(slotWithAttributeIds, () => $"slotWithAttributeIds is null !!! - {owner.toBasicString()}"); var result = new Result(); string err_msg; var item_attribute = item.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(item_attribute, () => $"item_attribute is null !!! - {owner.toBasicString()}"); var item_meta = item.getItemMeta(); NullReferenceCheckHelper.throwIfNull(item_meta, () => $"item_meta is null !!! - {owner.toBasicString()}"); var enchant = getEnchantData(item_attribute.Level, item_meta); if (null == enchant) { err_msg = $"Fail to get enchant data : {nameof(getEnchantData)}"; result.setFail(ServerErrorCode.ItemEnchantNotFoundInMeta, err_msg); Log.getLogger().error(err_msg); return result; } foreach (var each in slotWithAttributeIds) { if (false == EnumHelper.isDefined(each.Value)) { Log.getLogger().warn($"Invalid Ability Enum Type : {each.Value}"); continue; } // 배열의 위치를 AttributeType으로 활용하다보니 불필요한 예외 처리 코드를 작성해야 한다. !!! // Key-Value 구조로 바꾸고 싶지만... MetaHelper를 통해 메타 데이터를 가공하는 방법도 있다.... - kangms if (each.Key >= enchant.Count) { Log.getLogger().warn($"Ability Type outof range !!! : enchantCount:{enchant.Count} > slotWithAttributeIndex:{each.Key}"); continue; } setAbility((AttributeType)each.Value, enchant[each.Key], decrease); } return result; } public Result hasAbilities(Dictionary toCheckAbilities) { var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!! - {toBasicString()}"); ArgumentNullReferenceCheckHelper.throwIfNull(toCheckAbilities, () => $"toCheckAbilities is null !!! - {owner.toBasicString()}"); var result = new Result(); var err_msg = string.Empty; var ability_action = owner.getEntityAction(); NullReferenceCheckHelper.throwIfNull(ability_action, () => $"ability_action is null !!! - {toBasicString()}"); foreach (var each in toCheckAbilities) { var ability_name = each.Key; var ability_value = each.Value; if (false == EnumHelper.tryParse(ability_name, out var attribute_type)) { err_msg = $"Invalid Ability Enum Type : {attribute_type} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.ServerTypeInvalid, err_msg); Log.getLogger().error(err_msg); return result; } var curr_value = ability_action.getAbility(attribute_type); if (curr_value == null) { err_msg = $"Invalid Ability Enum Type : {attribute_type} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.ServerTypeInvalid, err_msg); Log.getLogger().error(err_msg); return result; } if (curr_value < ability_value) { err_msg = $"Not enough Ability !!! : AbilityName:{attribute_type}, currValue:{curr_value} >= reqValue:{ability_value} - {getOwner().toBasicString()}"; result.setFail(ServerErrorCode.AbilityNotEnough, err_msg); Log.getLogger().info(err_msg); return result; } } return result; } private IReadOnlyList? getEnchantData(int level, MetaAssets.ItemMetaData item) { ArgumentNullReferenceCheckHelper.throwIfNull(item, () => $"item is null !!! - {toBasicString()}"); if (false == MetaData.Instance._ItemLevelEnchantMetaTable.TryGetValue(level, out var enchant_by_level)) { return null; } var enchant = enchant_by_level.GetConsumeItem(item.Rarity); return enchant?.AttributeValues; } public AbilityInfo toAbilityInfo() { var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var ability_info = new AbilityInfo(); foreach(var each in m_abilities) { ability_info.Values.Add((Int16)each.Key, each.Value); } return ability_info; } } }