diff --git a/data/uking_functions.csv b/data/uking_functions.csv index c89accc2..4d42f3df 100644 --- a/data/uking_functions.csv +++ b/data/uking_functions.csv @@ -77855,17 +77855,17 @@ Address,Quality,Size,Name 0x0000007100e4167c,O,000100,_ZN4ksys3eco9Ecosystem18SingletonDisposer_D1Ev 0x0000007100e416e0,O,000108,_ZN4ksys3eco9Ecosystem18SingletonDisposer_D0Ev 0x0000007100e4174c,O,000208,_ZN4ksys3eco9Ecosystem14createInstanceEPN4sead4HeapE -0x0000007100e4181c,U,001056,Ecosystem::init +0x0000007100e4181c,O,001056,_ZN4ksys3eco9Ecosystem4initEPN4sead4HeapE 0x0000007100e41c3c,O,000004,_ZN4ksys3eco9Ecosystem4calcEv 0x0000007100e41c40,M,000292,_ZNK4ksys3eco9Ecosystem10getMapAreaERKNS0_10EcoMapInfoEff -0x0000007100e41d64,U,000636,Ecosystem::__auto1 +0x0000007100e41d64,O,000636,_ZNK4ksys3eco9Ecosystem12getAreaItemsEiNS0_12AreaItemTypeEPNS0_11AreaItemSetE 0x0000007100e41fe0,m,000384,_ZNK4ksys3eco9Ecosystem19getStatusEffectInfoENS0_12StatusEffectEiPNS0_16StatusEffectInfoE 0x0000007100e42160,O,000120,_ZNK4ksys3eco9Ecosystem16getAreaNameByNumEiPPKc 0x0000007100e421d8,O,000120,_ZNK4ksys3eco9Ecosystem19getClimateNameByNumEiPPKc 0x0000007100e42250,O,000120,_ZNK4ksys3eco9Ecosystem20getEnvSoundNameByNumEiPPKc 0x0000007100e422c8,m,000356,_ZNK4ksys3eco9Ecosystem17getEcoTraitsByNumEiPNS0_15EcosystemTraitsE -0x0000007100e4242c,U,000096, -0x0000007100e4248c,U,000104, +0x0000007100e4242c,O,000096,_ZN4ksys3eco9EcosystemD1Ev +0x0000007100e4248c,O,000104,_ZN4ksys3eco9EcosystemD0Ev 0x0000007100e424f4,O,000040,_ZN4ksys3eco11LevelSensorC1Ev 0x0000007100e4251c,O,000072,_ZN4ksys3eco11LevelSensorD1Ev 0x0000007100e42564,O,000080,_ZN4ksys3eco11LevelSensorD0Ev diff --git a/src/KingSystem/Ecosystem/ecoSystem.cpp b/src/KingSystem/Ecosystem/ecoSystem.cpp index 83fd3c8c..0e6fded4 100644 --- a/src/KingSystem/Ecosystem/ecoSystem.cpp +++ b/src/KingSystem/Ecosystem/ecoSystem.cpp @@ -1,7 +1,26 @@ #include "KingSystem/Ecosystem/ecoSystem.h" +#include "KingSystem/Resource/resLoadRequest.h" +#include "KingSystem/Utils/Byaml/Byaml.h" namespace ksys::eco { +constexpr const char* sAreaItemTypeStr[] = { + "Animal", + "Fish", + "Insect", + "Bird", + "Mushroom", + "Fruit", + "Mineral", + "Plant", + "Enemy", + "GrassCut", + "AutoCliffMaterial", + "AutoPlacementMaterial", + "RuinAutoPlacement", + "RainBonusMaterial", +}; + static const char* sStatusEffectNames[22] = {"StatusEffect", "LifeRecover", "LifeMaxUp", @@ -27,6 +46,55 @@ static const char* sStatusEffectNames[22] = {"StatusEffect", SEAD_SINGLETON_DISPOSER_IMPL(Ecosystem) +static void setEcoMapInfo(EcoMapInfo& info, const u8* data) { + info.mHeader = reinterpret_cast(data); + info.mRowOffsets = reinterpret_cast(info.mHeader + 1); + info.mRows = + reinterpret_cast(info.mRowOffsets) + sizeof(int) * info.mHeader->num_rows; +} + +void Ecosystem::init(sead::Heap* heap) { + res::LoadRequest req; + req.mRequester = "Ecosystem"; + + req._22 = false; + mHandles.mFieldMapArea.load("Ecosystem/FieldMapArea.beco", &req); + + req._22 = true; + mHandles.mAreaData.load("Ecosystem/AreaData.byml", &req); + + req._22 = false; + mHandles.mMapTower.load("Ecosystem/MapTower.beco", &req); + + req._22 = false; + mHandles.mStatusEffectList.load("Ecosystem/StatusEffectList.byml", &req); + + req._22 = false; + mHandles.mLoadBalancer.load("Ecosystem/LoadBalancer.beco", &req); + + auto* field_map_area = + sead::DynamicCast(mHandles.mFieldMapArea.getResource()); + setEcoMapInfo(mFieldMapArea, field_map_area->getRawData()); + + auto* map_tower = sead::DynamicCast(mHandles.mMapTower.getResource()); + setEcoMapInfo(mMapTower, map_tower->getRawData()); + + auto* load_balancer = + sead::DynamicCast(mHandles.mLoadBalancer.getResource()); + setEcoMapInfo(mLoadBalancer, load_balancer->getRawData()); + + auto* area_data = sead::DynamicCast(mHandles.mAreaData.getResource()); + mAreaDataIter = new (heap) al::ByamlIter(area_data->getRawData()); + mAreaDataSize = mAreaDataIter->getSize(); + + auto* status_effect_list = + sead::DynamicCast(mHandles.mStatusEffectList.getResource()); + mStatusEffectListIter = new (heap) al::ByamlIter(status_effect_list->getRawData()); + + mLevelSensor = new (heap) LevelSensor; + mLevelSensor->init(heap); +} + void Ecosystem::calc() {} // FP instructions rearranged. @@ -65,6 +133,64 @@ s32 Ecosystem::getMapArea(const EcoMapInfo& info, f32 posX, f32 posZ) const { } #endif +void Ecosystem::getAreaItems(s32 areaNum, AreaItemType type, AreaItemSet* out) const { + out->count = 0; + + if (areaNum < 0 || areaNum >= int(mAreaDataSize)) + return; + + al::ByamlIter area_iter; + if (!mAreaDataIter->tryGetIterByIndex(&area_iter, areaNum)) + return; + + al::ByamlIter items_iter; + if (!area_iter.tryGetIterByKey(&items_iter, sAreaItemTypeStr[u32(type)])) + return; + + out->count = items_iter.getSize(); + + for (int i = 0; i < out->count; ++i) { + al::ByamlIter item_iter; + if (!items_iter.tryGetIterByIndex(&item_iter, i)) + continue; + + auto& entry = out->items[i]; + + item_iter.tryGetStringByKey(&entry.name, "name"); + item_iter.tryGetFloatByKey(&entry.num, "num"); + + if (!item_iter.tryGetStringByKey(&entry.set, "set")) + entry.set = nullptr; + + if (!item_iter.tryGetFloatByKey(&entry.radius, "radius")) + entry.radius = 0.0; + + al::ByamlIter weapons_iter; + if (item_iter.tryGetIterByKey(&weapons_iter, "weapons")) { + // Fill in weapon information now. + for (int w_idx = 0; w_idx < weapons_iter.getSize(); ++w_idx) { + al::ByamlIter weapon_iter; + if (!weapons_iter.tryGetIterByIndex(&weapon_iter, w_idx)) + continue; + + auto& weapon = entry.weapons[w_idx]; + weapon_iter.tryGetStringByKey(&weapon.name, "name"); + if (!weapon_iter.tryGetFloatByKey(&weapon.prob, "prob")) { + // Try to get `prob` again, this time as an integer. + int prob; + if (weapon_iter.tryGetIntByKey(&prob, "prob")) + weapon.prob = prob; + else + weapon.prob = 100.0; + } + } + entry.num_weapons = weapons_iter.getSize(); + } else { + entry.num_weapons = 0; + } + } +} + void Ecosystem::getAreaNameByNum(s32 areaNum, const char** out) const { *out = nullptr; @@ -199,4 +325,6 @@ void Ecosystem::getEcoTraitsByNum(s32 areaNum, EcosystemTraits* out) const { } } +Ecosystem::~Ecosystem() = default; + } // namespace ksys::eco diff --git a/src/KingSystem/Ecosystem/ecoSystem.h b/src/KingSystem/Ecosystem/ecoSystem.h index dada5091..4b63bf2e 100644 --- a/src/KingSystem/Ecosystem/ecoSystem.h +++ b/src/KingSystem/Ecosystem/ecoSystem.h @@ -1,19 +1,27 @@ #pragma once #include +#include #include #include #include #include "KingSystem/Ecosystem/ecoLevelSensor.h" -#include "KingSystem/Utils/Byaml/Byaml.h" + +namespace al { +class ByamlIter; +} namespace ksys::eco { struct EcoMapHeader { - u32 unknown; + /// File magic (0x00112233). + u32 magic; s32 num_rows; s32 divisor; + u32 reserved; }; +KSYS_CHECK_SIZE_NX150(EcoMapHeader, 0x10); + struct Segment { s16 value; s16 length; @@ -21,14 +29,54 @@ struct Segment { class EcoMapInfo { public: - EcoMapHeader* mHeader; - u32* mRowOffsets; - char* mRows; + const EcoMapHeader* mHeader; + const u32* mRowOffsets; + const char* mRows; }; -enum ActorType {}; +enum class AreaItemType { + Animal, + Fish, + Insect, + Bird, + Mushroom, + Fruit, + Mineral, + Plant, + Enemy, + GrassCut, + AutoCliffMaterial, + AutoPlacementMaterial, + RuinAutoPlacement, + RainBonusMaterial, +}; -struct ActorSpawnInfo; +struct AreaWeapon { + /// Weapon name. + const char* name; + /// Probability of this weapon appearing (0 to 100). + float prob; +}; + +struct AreaItem { + /// Actor name. + const char* name; + /// Appearance weight. + float num; + /// Weapons carried by this actor (if this is an enemy). + sead::SafeArray weapons; + /// Number of valid entries in the `weapons` array. + int num_weapons; + /// Name of the set this item appears in. Typically used for small fruits like berries. + const char* set; + float radius; +}; + +struct AreaItemSet { + sead::SafeArray items; + /// Number of valid entries in the `items` array. + int count; +}; union StatusEffectVal { f32 _f32; @@ -94,14 +142,14 @@ private: virtual ~Ecosystem(); public: - void init(); + void init(sead::Heap* heap); void calc(); s32 getMapArea(const EcoMapInfo& info, f32 posX, f32 posZ) const; s32 getFieldMapArea(f32 x, f32 z) const { return getMapArea(mFieldMapArea, x, z); } - void getActorSpawnInfo(s32 areaNum, ActorType actorTypeIdx, ActorSpawnInfo* out) const; + void getAreaItems(s32 areaNum, AreaItemType type, AreaItemSet* out) const; void getStatusEffectInfo(StatusEffect statusEffectIdx, s32 idx, StatusEffectInfo* out) const; void getAreaNameByNum(s32 areaNum, const char** out) const; void getClimateNameByNum(s32 areaNum, const char** out) const; @@ -109,11 +157,14 @@ public: void getEcoTraitsByNum(s32 areaNum, EcosystemTraits* out) const; private: - res::Handle mFieldMapAreaFile; - res::Handle mAreaDataFile; - res::Handle mMapTowerFile; - res::Handle mStatusEffectListFile; - res::Handle mLoadBalancerFile; + struct Handles { + res::Handle mFieldMapArea; + res::Handle mAreaData; + res::Handle mMapTower; + res::Handle mStatusEffectList; + res::Handle mLoadBalancer; + }; + Handles mHandles; al::ByamlIter* mAreaDataIter{}; u32 mAreaDataSize{};