summaryrefslogtreecommitdiff
path: root/src/Game/Actor
diff options
context:
space:
mode:
authorLéo Lam <leo@leolam.fr>2021-01-09 15:45:07 +0100
committerLéo Lam <leo@leolam.fr>2021-01-10 02:14:31 +0100
commit29817a3e21f73d061f68e80d372b21060abd0efd (patch)
tree753617ce408b1f68002ea18f529e9429c2964c53 /src/Game/Actor
parent65261d02a6f119ea788396ebe0c287e4080873bf (diff)
uking/act: Add most of the weapon modifier utils
Diffstat (limited to 'src/Game/Actor')
-rw-r--r--src/Game/Actor/CMakeLists.txt4
-rw-r--r--src/Game/Actor/actWeapon.cpp474
-rw-r--r--src/Game/Actor/actWeapon.h142
3 files changed, 620 insertions, 0 deletions
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 <random/seadGlobalRandom.h>
+#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<void>(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<void>(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 <basis/seadTypes.h>
+#include <prim/seadSafeString.h>
+#include <prim/seadTypedBitFlag.h>
+
+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<WeaponModifier> 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