diff --git a/data/uking_functions.csv b/data/uking_functions.csv index 916d1fb4..57b04dbb 100644 --- a/data/uking_functions.csv +++ b/data/uking_functions.csv @@ -19684,36 +19684,36 @@ 0x00000071002def90,sub_71002DEF90,80, 0x00000071002defe0,sub_71002DEFE0,88, 0x00000071002df038,Weapon::construct,68, -0x00000071002df07c,sub_71002DF07C,40, -0x00000071002df0a4,sub_71002DF0A4,44, -0x00000071002df0d0,SharpWeaponTypeValuePair::getAddLifeBonus,32, -0x00000071002df0f0,return100,8, -0x00000071002df0f8,getPorchSwordFlagAndValuePair,72, -0x00000071002df140,getPorchShieldFlagAndValuePair,72, -0x00000071002df188,getPorchBowFlagAndValuePair,72, -0x00000071002df1d0,setPorchSwordFlagAndValuePair,56, -0x00000071002df208,setPorchShieldFlagAndValuePair,56, -0x00000071002df240,setPorchBowFlagAndValuePair,56, -0x00000071002df278,sub_71002DF278,72, -0x00000071002df2c0,sub_71002DF2C0,72, -0x00000071002df308,sub_71002DF308,72, -0x00000071002df350,sub_71002DF350,56, -0x00000071002df388,sub_71002DF388,56, -0x00000071002df3c0,sub_71002DF3C0,56, -0x00000071002df3f8,addParamsAddParamAndAddSpecialFlag,148, -0x00000071002df48c,SharpWeaponTypeValuePair::ctor,8, +0x00000071002df07c,sub_71002DF07C,40,_ZN5uking3act18WeaponModifierInfoC1ERKNS_2ui9PouchItemE? +0x00000071002df0a4,sub_71002DF0A4,44,_ZN5uking3act18WeaponModifierInfo8fromItemERKNS_2ui9PouchItemE? +0x00000071002df0d0,SharpWeaponTypeValuePair::getAddLifeBonus,32,_ZNK5uking3act18WeaponModifierInfo10getAddLifeEv +0x00000071002df0f0,return100,8,_ZN5uking3act18WeaponModifierInfo17getLifeMultiplierEv +0x00000071002df0f8,getPorchSwordFlagAndValuePair,72,_ZN5uking3act18WeaponModifierInfo18loadPorchSwordFlagEi +0x00000071002df140,getPorchShieldFlagAndValuePair,72,_ZN5uking3act18WeaponModifierInfo19loadPorchShieldFlagEi +0x00000071002df188,getPorchBowFlagAndValuePair,72,_ZN5uking3act18WeaponModifierInfo16loadPorchBowFlagEi +0x00000071002df1d0,setPorchSwordFlagAndValuePair,56,_ZN5uking3act18WeaponModifierInfo18savePorchSwordFlagEi +0x00000071002df208,setPorchShieldFlagAndValuePair,56,_ZN5uking3act18WeaponModifierInfo19savePorchShieldFlagEi +0x00000071002df240,setPorchBowFlagAndValuePair,56,_ZN5uking3act18WeaponModifierInfo16savePorchBowFlagEi +0x00000071002df278,sub_71002DF278,72,_ZN5uking3act18WeaponModifierInfo23loadEquipStandSwordFlagEi +0x00000071002df2c0,sub_71002DF2C0,72,_ZN5uking3act18WeaponModifierInfo24loadEquipStandShieldFlagEi +0x00000071002df308,sub_71002DF308,72,_ZN5uking3act18WeaponModifierInfo21loadEquipStandBowFlagEi +0x00000071002df350,sub_71002DF350,56,_ZN5uking3act18WeaponModifierInfo23saveEquipStandSwordFlagEi +0x00000071002df388,sub_71002DF388,56,_ZN5uking3act18WeaponModifierInfo24saveEquipStandShieldFlagEi +0x00000071002df3c0,sub_71002DF3C0,56,_ZN5uking3act18WeaponModifierInfo21saveEquipStandBowFlagEi +0x00000071002df3f8,addParamsAddParamAndAddSpecialFlag,148,_ZN5uking3act18WeaponModifierInfo17addModifierParamsERN4ksys3act13InstParamPackE +0x00000071002df48c,SharpWeaponTypeValuePair::ctor,8,_ZN5uking3act18WeaponModifierInfo3setEjj 0x00000071002df494,SharpWeaponTypeValuePair::addEventParam,92, 0x00000071002df4f0,ai::ActorAI::x,104, -0x00000071002df558,WeaponModifierRanges::loadBlue,212, -0x00000071002df62c,WeaponModifierRanges::loadYellow,300, -0x00000071002df758,tbox::getWeaponBonusBlueAmiibo,128, -0x00000071002df7d8,tbox::determineWeaponBonusAmiibo,356, -0x00000071002df93c,tbox::getWeaponBonusYellowAmiibo,128, -0x00000071002df9bc,getWeaponBonusBlue,480, -0x00000071002dfb9c,determineWeaponBonus,628, -0x00000071002dfe10,weaponBonusStuff_1,292, -0x00000071002dff34,tbox::getWeaponBonusYellow,480, -0x00000071002e0114,weaponBonusStuff,516, +0x00000071002df558,WeaponModifierRanges::loadBlue,212,_ZN5uking3act20WeaponModifierRanges12loadTierBlueERKN4sead14SafeStringBaseIcEE +0x00000071002df62c,WeaponModifierRanges::loadYellow,300,_ZN5uking3act20WeaponModifierRanges14loadTierYellowERKN4sead14SafeStringBaseIcEE +0x00000071002df758,tbox::getWeaponBonusBlueAmiibo,128,_ZN5uking3act18WeaponModifierInfo28pickRandomBlueModifierAmiiboERKN4sead14SafeStringBaseIcEE +0x00000071002df7d8,tbox::determineWeaponBonusAmiibo,356,_ZN5uking3act18WeaponModifierInfo24pickRandomModifierAmiiboERKNS0_20WeaponModifierRangesE +0x00000071002df93c,tbox::getWeaponBonusYellowAmiibo,128,_ZN5uking3act18WeaponModifierInfo30pickRandomYellowModifierAmiiboERKN4sead14SafeStringBaseIcEE +0x00000071002df9bc,getWeaponBonusBlue,480,_ZN5uking3act18WeaponModifierInfo26pickRandomBlueModifierTboxERKN4sead14SafeStringBaseIcEE! +0x00000071002dfb9c,determineWeaponBonus,628,_ZN5uking3act18WeaponModifierInfo18pickRandomModifierERKNS0_20WeaponModifierRangesE +0x00000071002dfe10,weaponBonusStuff_1,292,_ZN5uking3act18WeaponModifierInfo27pickRandomBlueModifierActorERKN4ksys3act20ActorConstDataAccessE? +0x00000071002dff34,tbox::getWeaponBonusYellow,480,_ZN5uking3act18WeaponModifierInfo28pickRandomYellowModifierTboxERKN4sead14SafeStringBaseIcEE! +0x00000071002e0114,weaponBonusStuff,516,_ZN5uking3act18WeaponModifierInfo29pickRandomYellowModifierActorERKN4ksys3act20ActorConstDataAccessE! 0x00000071002e0318,weaponBonusStuff_0,404, 0x00000071002e04ac,Weapon::ctor,876, 0x00000071002e0818,Weapon::invokedEmitBlinkEffect,108, @@ -19977,7 +19977,7 @@ 0x00000071002f1d10,sub_71002F1D10,40, 0x00000071002f1d38,sub_71002F1D38,8, 0x00000071002f1d40,sub_71002F1D40,40, -0x00000071002f1d68,WeaponModifierRanges::getRandomBonus,512, +0x00000071002f1d68,WeaponModifierRanges::getRandomBonus,512,_ZNK5uking3act20WeaponModifierRanges17getRandomModifierEv 0x00000071002f1f68,sub_71002F1F68,204, 0x00000071002f2034,sub_71002F2034,92, 0x00000071002f2090,sub_71002F2090,372, diff --git a/src/Game/Actor/CMakeLists.txt b/src/Game/Actor/CMakeLists.txt new file mode 100644 index 00000000..08ef56ef --- /dev/null +++ b/src/Game/Actor/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(uking PRIVATE + actWeapon.cpp + actWeapon.h +) diff --git a/src/Game/Actor/actWeapon.cpp b/src/Game/Actor/actWeapon.cpp new file mode 100644 index 00000000..f9080945 --- /dev/null +++ b/src/Game/Actor/actWeapon.cpp @@ -0,0 +1,474 @@ +#include "Game/Actor/actWeapon.h" +#include +#include "Game/UI/uiPauseMenuDataMgr.h" +#include "KingSystem/ActorSystem/actActorConstDataAccess.h" +#include "KingSystem/ActorSystem/actGlobalParameter.h" +#include "KingSystem/ActorSystem/actInfoCommon.h" +#include "KingSystem/ActorSystem/actInfoData.h" +#include "KingSystem/ActorSystem/actInstParamPack.h" +#include "KingSystem/GameData/gdtCommonFlagsUtils.h" +#include "KingSystem/Resource/GeneralParamList/resGParamListObjectGlobal.h" +#include "KingSystem/Resource/GeneralParamList/resGParamListObjectWeaponCommon.h" +#include "KingSystem/Resource/resResourceGParamList.h" +#include "KingSystem/Utils/Byaml/Byaml.h" + +namespace uking::act { + +WeaponModifierInfo::WeaponModifierInfo(const ui::PouchItem& item) { + fromItem(item); +} + +void WeaponModifierInfo::fromItem(const ui::PouchItem& item) { + if (item.getType() <= ui::PouchItemType::Shield) { + set(item.getWeaponData().mAddType, item.getWeaponData().mAddValue); + } else { + flags.setDirect(0); + value = 0; + } +} + +int WeaponModifierInfo::getAddLife() const { + if (flags.isOff(WeaponModifier::AddLife)) + return 0; + return getLifeMultiplier() * value; +} + +int WeaponModifierInfo::getLifeMultiplier() { + return 100; +} + +void WeaponModifierInfo::loadPorchSwordFlag(int idx) { + set(ksys::gdt::getFlag_PorchSword_FlagSp(idx), ksys::gdt::getFlag_PorchSword_ValueSp(idx)); +} + +void WeaponModifierInfo::loadPorchShieldFlag(int idx) { + set(ksys::gdt::getFlag_PorchShield_FlagSp(idx), ksys::gdt::getFlag_PorchShield_ValueSp(idx)); +} + +void WeaponModifierInfo::loadPorchBowFlag(int idx) { + set(ksys::gdt::getFlag_PorchBow_FlagSp(idx), ksys::gdt::getFlag_PorchBow_ValueSp(idx)); +} + +void WeaponModifierInfo::savePorchSwordFlag(int idx) { + ksys::gdt::setFlag_PorchSword_FlagSp(flags.getDirect(), idx); + ksys::gdt::setFlag_PorchSword_ValueSp(value, idx); +} + +void WeaponModifierInfo::savePorchShieldFlag(int idx) { + ksys::gdt::setFlag_PorchShield_FlagSp(flags.getDirect(), idx); + ksys::gdt::setFlag_PorchShield_ValueSp(value, idx); +} + +void WeaponModifierInfo::savePorchBowFlag(int idx) { + ksys::gdt::setFlag_PorchBow_FlagSp(flags.getDirect(), idx); + ksys::gdt::setFlag_PorchBow_ValueSp(value, idx); +} + +void WeaponModifierInfo::loadEquipStandSwordFlag(int idx) { + set(ksys::gdt::getFlag_EquipStandSword_FlagSp(idx), + ksys::gdt::getFlag_EquipStandSword_ValueSp(idx)); +} + +void WeaponModifierInfo::loadEquipStandShieldFlag(int idx) { + set(ksys::gdt::getFlag_EquipStandShield_FlagSp(idx), + ksys::gdt::getFlag_EquipStandShield_ValueSp(idx)); +} + +void WeaponModifierInfo::loadEquipStandBowFlag(int idx) { + set(ksys::gdt::getFlag_EquipStandBow_FlagSp(idx), + ksys::gdt::getFlag_EquipStandBow_ValueSp(idx)); +} + +void WeaponModifierInfo::saveEquipStandSwordFlag(int idx) { + ksys::gdt::setFlag_EquipStandSword_FlagSp(flags.getDirect(), idx); + ksys::gdt::setFlag_EquipStandSword_ValueSp(value, idx); +} + +void WeaponModifierInfo::saveEquipStandShieldFlag(int idx) { + ksys::gdt::setFlag_EquipStandShield_FlagSp(flags.getDirect(), idx); + ksys::gdt::setFlag_EquipStandShield_ValueSp(value, idx); +} + +void WeaponModifierInfo::saveEquipStandBowFlag(int idx) { + ksys::gdt::setFlag_EquipStandBow_FlagSp(flags.getDirect(), idx); + ksys::gdt::setFlag_EquipStandBow_ValueSp(value, idx); +} + +void WeaponModifierInfo::addModifierParams(ksys::act::InstParamPack& params) { + params->add(value, "AddParam"); + params->add(flags.getDirect(), "AddSpecialFlag"); +} + +void WeaponModifierInfo::set(u32 type_, u32 value_) { + flags.setDirect(type_); + value = value_; +} + +bool WeaponModifierRanges::loadTierBlue(const sead::SafeString& actor) { + auto* info = ksys::act::InfoData::instance(); + if (!info) + return false; + + al::ByamlIter iter; + if (!info->getActorIter(&iter, actor.cstr())) + return false; + + addAtkMin = ksys::act::getWeaponCommonSharpWeaponAddAtkMin(iter); + addAtkMax = ksys::act::getWeaponCommonSharpWeaponAddAtkMax(iter); + + addLifeMin = ksys::act::getWeaponCommonSharpWeaponAddLifeMin(iter); + addLifeMax = ksys::act::getWeaponCommonSharpWeaponAddLifeMax(iter); + + addCrit = ksys::act::getWeaponCommonSharpWeaponAddCrit(iter); + + addGuardMin = ksys::act::getWeaponCommonSharpWeaponAddGuardMin(iter); + addGuardMax = ksys::act::getWeaponCommonSharpWeaponAddGuardMax(iter); + + return true; +} + +bool WeaponModifierRanges::loadTierYellow(const sead::SafeString& actor) { + auto* info = ksys::act::InfoData::instance(); + if (!info) + return false; + + al::ByamlIter iter; + if (!info->getActorIter(&iter, actor.cstr())) + return false; + + isTierYellow = true; + + addAtkMin = ksys::act::getWeaponCommonPoweredSharpAddAtkMin(iter); + addAtkMax = ksys::act::getWeaponCommonPoweredSharpAddAtkMax(iter); + + addLifeMin = ksys::act::getWeaponCommonPoweredSharpAddLifeMin(iter); + addLifeMax = ksys::act::getWeaponCommonPoweredSharpAddLifeMax(iter); + + addGuardMin = ksys::act::getWeaponCommonPoweredSharpAddGuardMin(iter); + addGuardMax = ksys::act::getWeaponCommonPoweredSharpAddGuardMax(iter); + + addThrowMin = ksys::act::getWeaponCommonPoweredSharpAddThrowMin(iter); + addThrowMax = ksys::act::getWeaponCommonPoweredSharpAddThrowMax(iter); + + addSpreadFire = ksys::act::getWeaponCommonPoweredSharpAddSpreadFire(iter); + addZoomRapid = ksys::act::getWeaponCommonPoweredSharpAddZoomRapid(iter); + + addRapidFireMin = ksys::act::getWeaponCommonPoweredSharpAddRapidFireMin(iter); + addRapidFireMax = ksys::act::getWeaponCommonPoweredSharpAddRapidFireMax(iter); + + addSurfMaster = ksys::act::getWeaponCommonPoweredSharpAddSurfMaster(iter); + + return true; +} + +WeaponModifier WeaponModifierRanges::getRandomModifier() const { + u32 max = 1; + WeaponModifier modifier = WeaponModifier::None; + + if (addAtkMin > 0) { + modifier = WeaponModifier::AddAtk; + ++max; + } + + if (addLifeMin > 0) { + const u32 x = sead::GlobalRandom::instance()->getU32(max); + if (x == 0) { + modifier = WeaponModifier::AddLife; + ++max; + } + } + + if (addCrit && sead::GlobalRandom::instance()->getU32(max) == 0) { + modifier = WeaponModifier::AddCrit; + ++max; + } + + if (addGuardMin > 0 && sead::GlobalRandom::instance()->getU32(max) == 0) { + modifier = WeaponModifier::AddGuard; + ++max; + } + + if ((addThrowMin != 1.0 || addThrowMax != 1.0) && + sead::GlobalRandom::instance()->getU32(max) == 0) { + modifier = WeaponModifier::AddThrow; + ++max; + } + + if (addSpreadFire && sead::GlobalRandom::instance()->getU32(max) == 0) { + modifier = WeaponModifier::AddSpreadFire; + ++max; + } + + if (addZoomRapid && sead::GlobalRandom::instance()->getU32(max) == 0) { + modifier = WeaponModifier::AddZoomRapid; + ++max; + } + + if ((addRapidFireMin != 1.0 || addRapidFireMax != 1.0) && + sead::GlobalRandom::instance()->getU32(max) == 0) { + modifier = WeaponModifier::AddRapidFire; + ++max; + } + + if (addSurfMaster && sead::GlobalRandom::instance()->getU32(max) == 0) { + modifier = WeaponModifier::AddSurfMaster; + ++max; + } + + return modifier; +} + +bool WeaponModifierRanges::isConfigValid() const { + if (addAtkMin <= 0 && addLifeMin <= 0 && !addCrit && addGuardMin <= 0 && addThrowMin == 1.0 && + addThrowMax == 1.0 && !addSpreadFire && !addZoomRapid && addRapidFireMin == 1.0 && + addRapidFireMax == 1.0 && !addSurfMaster) { + return false; + } + + if (addAtkMin < 0 || (addAtkMin > 0 && addAtkMin > addAtkMax)) + return false; + + if (addLifeMin < 0 || (addLifeMin > 0 && addLifeMin > addLifeMax)) + return false; + + if (addThrowMin < 0.0 || (addThrowMin > 0.0 && addThrowMin > addThrowMax)) + return false; + + if (addRapidFireMin < 0.0 || (addRapidFireMin > 0.0 && addRapidFireMin > addRapidFireMax)) + return false; + + return true; +} + +bool WeaponModifierInfo::pickRandomBlueModifierAmiibo(const sead::SafeString& actor) { + if (!ksys::act::InfoData::instance()) + return false; + + WeaponModifierRanges ranges; + if (!ranges.loadTierBlue(actor)) + return false; + + return pickRandomModifierAmiibo(ranges); +} + +bool WeaponModifierInfo::pickRandomModifierAmiibo(const WeaponModifierRanges& ranges) { + const auto modifier = ranges.getRandomModifier(); + switch (modifier) { + case WeaponModifier::None: + return false; + case WeaponModifier::AddAtk: + if (ranges.addAtkMax > 0) + setModifier(WeaponModifier::AddAtk, ranges.addAtkMax); + return true; + case WeaponModifier::AddLife: + if (ranges.addLifeMax > 0) + setModifier(WeaponModifier::AddLife, ranges.addLifeMax); + return true; + case WeaponModifier::AddThrow: + if (ranges.addThrowMax > 0.0) + setModifierFloat(WeaponModifier::AddThrow, ranges.addThrowMax); + return true; + case WeaponModifier::AddSpreadFire: + setModifier(WeaponModifier::AddSpreadFire, 5); + return true; + case WeaponModifier::AddRapidFire: + if (ranges.addRapidFireMin > 0.0) + setModifierFloat(WeaponModifier::AddRapidFire, ranges.addRapidFireMin); + return true; + case WeaponModifier::AddSurfMaster: { + if (!ksys::act::GlobalParameter::instance()) + return true; + auto* param = ksys::act::GlobalParameter::instance()->getGlobalParam(); + if (param && param->mShieldSurfMasterFrictionRatio.ref() > 0.0) { + setModifierFloat(WeaponModifier::AddSurfMaster, + param->mShieldSurfMasterFrictionRatio.ref()); + } + return true; + } + case WeaponModifier::AddGuard: + if (ranges.addGuardMax > 0) { + /// @bug This is supposed to be WeaponModifier::AddGuard. + setModifier(WeaponModifier::AddLife, ranges.addGuardMax); + } + return true; + default: + flags = modifier; + value = 0; + return true; + } +} + +bool WeaponModifierInfo::pickRandomYellowModifierAmiibo(const sead::SafeString& actor) { + if (!ksys::act::InfoData::instance()) + return false; + + WeaponModifierRanges ranges; + if (!ranges.loadTierYellow(actor)) + return false; + + return pickRandomModifierAmiibo(ranges); +} + +// NON_MATCHING: isConfigValid() somehow does not match when inlined (but matches the Wii U version) +bool WeaponModifierInfo::pickRandomBlueModifierTbox(const sead::SafeString& actor) { + auto* info = ksys::act::InfoData::instance(); + if (!info) + return false; + + al::ByamlIter iter; + if (!info->getActorIter(&iter, actor.cstr())) + return false; + + WeaponModifierRanges ranges; + if (!ranges.loadTierBlue(actor)) + return false; + + if (!ranges.isConfigValid()) + return false; + + return pickRandomModifier(ranges); +} + +bool WeaponModifierInfo::pickRandomModifier(const WeaponModifierRanges& ranges) { + const auto modifier = ranges.getRandomModifier(); + if (modifier == WeaponModifier::None) + return false; + + if (ranges.isTierYellow) + flags.set(WeaponModifier::IsYellow); + + switch (modifier) { + case WeaponModifier::AddAtk: { + const auto min = ranges.addAtkMin; + const auto max = ranges.addAtkMax; + const auto val = sead::GlobalRandom::instance()->getS32Range(min, max + 1); + if (val > 0) + setModifier(WeaponModifier::AddAtk, val); + return true; + } + case WeaponModifier::AddLife: { + const auto min = ranges.addLifeMin; + const auto max = ranges.addLifeMax; + const auto val = sead::GlobalRandom::instance()->getS32Range(min, max + 1); + if (val > 0) + setModifier(WeaponModifier::AddLife, val); + return true; + } + case WeaponModifier::AddThrow: { + const auto val = + sead::GlobalRandom::instance()->getF32Range(ranges.addThrowMin, ranges.addThrowMax); + if (val > 0) + setModifierFloat(WeaponModifier::AddThrow, val); + return true; + } + case WeaponModifier::AddSpreadFire: + setModifier(WeaponModifier::AddSpreadFire, 5); + return true; + case WeaponModifier::AddRapidFire: { + const auto val = sead::GlobalRandom::instance()->getF32Range(ranges.addRapidFireMin, + ranges.addRapidFireMax); + if (val > 0) + setModifierFloat(WeaponModifier::AddRapidFire, val); + return true; + } + case WeaponModifier::AddSurfMaster: { + if (!ksys::act::GlobalParameter::instance()) + return true; + auto* param = ksys::act::GlobalParameter::instance()->getGlobalParam(); + if (param && param->mShieldSurfMasterFrictionRatio.ref() > 0.0) { + setModifierFloat(WeaponModifier::AddSurfMaster, + param->mShieldSurfMasterFrictionRatio.ref()); + } + return true; + } + case WeaponModifier::AddGuard: { + const auto min = ranges.addGuardMin; + const auto max = ranges.addGuardMax; + const auto val = sead::GlobalRandom::instance()->getS32Range(min, max + 1); + if (val > 0) + setModifier(WeaponModifier::AddGuard, val); + return true; + } + default: + flags = modifier; + value = 0; + return true; + } +} + +bool WeaponModifierRanges::loadTierBlue(const ksys::res::GParamList& gparamlist) { + const auto* param = gparamlist.getWeaponCommon(); + addAtkMin = param->mSharpWeaponAddAtkMin.ref(); + addAtkMax = param->mSharpWeaponAddAtkMax.ref(); + addLifeMin = param->mSharpWeaponAddLifeMin.ref(); + addLifeMax = param->mSharpWeaponAddLifeMax.ref(); + addCrit = param->mSharpWeaponAddCrit.ref(); + addGuardMin = param->mSharpWeaponAddGuardMin.ref(); + addGuardMax = param->mSharpWeaponAddGuardMax.ref(); + return true; +} + +// NON_MATCHING: isConfigValid() somehow does not match when inlined (but matches the Wii U version) +bool WeaponModifierInfo::pickRandomBlueModifierActor(const ksys::act::ActorConstDataAccess& acc) { + WeaponModifierRanges ranges; + const auto& gparamlist = *acc.getGParamList(); + if (ranges.loadTierBlue(gparamlist)) { + static_cast(acc.getName()); + if (ranges.isConfigValid()) + return pickRandomModifier(ranges); + } + return false; +} + +// NON_MATCHING: isConfigValid() somehow does not match when inlined (but matches the Wii U version) +bool WeaponModifierInfo::pickRandomYellowModifierTbox(const sead::SafeString& actor) { + auto* info = ksys::act::InfoData::instance(); + if (!info) + return false; + + al::ByamlIter iter; + if (!info->getActorIter(&iter, actor.cstr())) + return false; + + WeaponModifierRanges ranges; + if (!ranges.loadTierYellow(actor)) + return false; + + if (!ranges.isConfigValid()) + return false; + + return pickRandomModifier(ranges); +} + +bool WeaponModifierRanges::loadTierYellow(const ksys::res::GParamList& gparamlist) { + const auto* param = gparamlist.getWeaponCommon(); + isTierYellow = true; + addAtkMin = param->mPoweredSharpAddAtkMin.ref(); + addAtkMax = param->mPoweredSharpAddAtkMax.ref(); + addLifeMin = param->mPoweredSharpAddLifeMin.ref(); + addLifeMax = param->mPoweredSharpAddLifeMax.ref(); + addGuardMin = param->mPoweredSharpWeaponAddGuardMin.ref(); + addGuardMax = param->mPoweredSharpWeaponAddGuardMax.ref(); + addThrowMin = param->mPoweredSharpAddThrowMin.ref(); + addThrowMax = param->mPoweredSharpAddThrowMax.ref(); + addSpreadFire = param->mPoweredSharpAddSpreadFire.ref(); + addZoomRapid = param->mPoweredSharpAddZoomRapid.ref(); + addRapidFireMin = param->mPoweredSharpAddRapidFireMin.ref(); + addRapidFireMax = param->mPoweredSharpAddRapidFireMax.ref(); + addSurfMaster = param->mPoweredSharpAddSurfMaster.ref(); + return true; +} + +// NON_MATCHING: isConfigValid() somehow does not match when inlined (but matches the Wii U version) +bool WeaponModifierInfo::pickRandomYellowModifierActor(const ksys::act::ActorConstDataAccess& acc) { + WeaponModifierRanges ranges; + const auto& gparamlist = *acc.getGParamList(); + if (ranges.loadTierYellow(gparamlist)) { + static_cast(acc.getName()); + if (ranges.isConfigValid()) + return pickRandomModifier(ranges); + } + return false; +} + +} // namespace uking::act diff --git a/src/Game/Actor/actWeapon.h b/src/Game/Actor/actWeapon.h new file mode 100644 index 00000000..2ccedd0f --- /dev/null +++ b/src/Game/Actor/actWeapon.h @@ -0,0 +1,142 @@ +#pragma once + +#include +#include +#include + +namespace ksys::act { +class ActorConstDataAccess; +class InstParamPack; +namespace ai { +struct InlineParamPack; +} +} // namespace ksys::act + +namespace ksys::evt { +class OrderParam; +} + +namespace ksys::res { +class GParamList; +} + +namespace uking::ui { +class PouchItem; +} + +namespace uking::act { + +enum class WeaponModifier : u32 { + None = 0, + /// Attack Up (swords and bows) + AddAtk = 0x1, + /// Durability Up + AddLife = 0x2, + + /// Critical Hit (swords) + AddCrit = 0x4, + /// Long Throw (swords) + AddThrow = 0x8, + + /// Multi-shot (bows) + AddSpreadFire = 0x10, + /// ??? (bows) + AddZoomRapid = 0x20, + /// Quick Shot (bows) + AddRapidFire = 0x40, + + /// Unused bonus type (shields) + AddSurfMaster = 0x80, + /// Shield Guard Up (shields) + AddGuard = 0x100, + + /// Whether this is a yellow tier (PoweredSharp) modifier. + IsYellow = 0x80000000, +}; + +struct WeaponModifierRanges; + +struct WeaponModifierInfo { + explicit WeaponModifierInfo(const ui::PouchItem& item); + void fromItem(const ui::PouchItem& item); + + int getAddLife() const; + static int getLifeMultiplier(); + + void loadPorchSwordFlag(int idx); + void loadPorchShieldFlag(int idx); + void loadPorchBowFlag(int idx); + + void savePorchSwordFlag(int idx); + void savePorchShieldFlag(int idx); + void savePorchBowFlag(int idx); + + void loadEquipStandSwordFlag(int idx); + void loadEquipStandShieldFlag(int idx); + void loadEquipStandBowFlag(int idx); + + void saveEquipStandSwordFlag(int idx); + void saveEquipStandShieldFlag(int idx); + void saveEquipStandBowFlag(int idx); + + void addModifierParams(ksys::act::InstParamPack& params); + void set(u32 type_, u32 value_); + static void addModifierParams(WeaponModifierInfo* self, ksys::evt::OrderParam& params); + static void addModifierParams(WeaponModifierInfo* self, ksys::act::ai::InlineParamPack& params); + + bool pickRandomBlueModifierAmiibo(const sead::SafeString& actor); + bool pickRandomYellowModifierAmiibo(const sead::SafeString& actor); + bool pickRandomModifierAmiibo(const WeaponModifierRanges& ranges); + + bool pickRandomModifier(const WeaponModifierRanges& ranges); + + bool pickRandomBlueModifierTbox(const sead::SafeString& actor); + bool pickRandomYellowModifierTbox(const sead::SafeString& actor); + + bool pickRandomBlueModifierActor(const ksys::act::ActorConstDataAccess& acc); + bool pickRandomYellowModifierActor(const ksys::act::ActorConstDataAccess& acc); + + void setModifier(WeaponModifier modifier, s32 value_) { + flags.setDirect((flags.getDirect() & u32(WeaponModifier::IsYellow)) | u32(modifier)); + value = value_; + } + + void setModifierFloat(WeaponModifier modifier, f32 value_) { + flags.setDirect((flags.getDirect() & u32(WeaponModifier::IsYellow)) | u32(modifier)); + value = value_ * 1000.0f; + } + + sead::TypedBitFlag flags; + s32 value; +}; + +struct WeaponModifierRanges { + bool loadTierBlue(const sead::SafeString& actor); + bool loadTierYellow(const sead::SafeString& actor); + + bool loadTierBlue(const ksys::res::GParamList& gparamlist); + bool loadTierYellow(const ksys::res::GParamList& gparamlist); + + WeaponModifier getRandomModifier() const; + bool isConfigValid() const; + + int addAtkMin = 0; + int addAtkMax = 0; + int addLifeMin = 0; + int addLifeMax = 0; + bool addCrit = false; + int addGuardMin = 0; + int addGuardMax = 0; + float addThrowMin = 1.0; + float addThrowMax = 1.0; + bool addSpreadFire = false; + bool addZoomRapid = false; + float addRapidFireMin = 1.0; + float addRapidFireMax = 1.0; + bool addSurfMaster = false; + bool isTierYellow = false; +}; + +// TODO: Weapon class + +} // namespace uking::act diff --git a/src/Game/CMakeLists.txt b/src/Game/CMakeLists.txt index 0dc60a1d..542314ac 100644 --- a/src/Game/CMakeLists.txt +++ b/src/Game/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(Actor) add_subdirectory(AI) add_subdirectory(DLC) add_subdirectory(UI) diff --git a/src/Game/UI/uiPauseMenuDataMgr.h b/src/Game/UI/uiPauseMenuDataMgr.h index 23b4fbb1..c6026e80 100644 --- a/src/Game/UI/uiPauseMenuDataMgr.h +++ b/src/Game/UI/uiPauseMenuDataMgr.h @@ -89,25 +89,6 @@ struct CookTagInfo { class PouchItem { public: - PouchItem(); - virtual ~PouchItem() = default; - - PouchItemType getType() const { return mType; } - bool isValid() const { return mValid; } - u8 get25() const { return _25; } - const sead::SafeString& getName() const { return mName; } - - // This is only valid if the item is not a weapon. - s32 getCount() const { return getValue(); } - - s32 getValue() const { return mValue; } - void setValue(s32 value) { mValue = value; } - - static auto getListNodeOffset() { return offsetof(PouchItem, mListNode); } - -private: - friend class PauseMenuDataMgr; - struct CookData { int mStaminaRecoverX; int mStaminaRecoverY; @@ -123,6 +104,33 @@ private: u32 e; }; + PouchItem(); + virtual ~PouchItem() = default; + + PouchItemType getType() const { return mType; } + bool isValid() const { return mValid; } + u8 get25() const { return _25; } + const sead::SafeString& getName() const { return mName; } + + // This is only valid if the item is not a weapon. + s32 getCount() const { return getValue(); } + + s32 getValue() const { return mValue; } + void setValue(s32 value) { mValue = value; } + + // Only valid if this is not a weapon. + CookData& getCookData() { return mData.cook; } + const CookData& getCookData() const { return mData.cook; } + + // Only valid if this is a weapon. + WeaponData& getWeaponData() { return mData.weapon; } + const WeaponData& getWeaponData() const { return mData.weapon; } + + static auto getListNodeOffset() { return offsetof(PouchItem, mListNode); } + +private: + friend class PauseMenuDataMgr; + union Data { CookData cook; WeaponData weapon;