diff --git a/src/Application/Game.cpp b/src/Application/Game.cpp index f3c2ac147a06..61f10dae95b6 100644 --- a/src/Application/Game.cpp +++ b/src/Application/Game.cpp @@ -378,6 +378,8 @@ void Game_StartDialogue(unsigned int actor_id) { } void Game_StartHirelingDialogue(unsigned int hireling_id) { + assert(hireling_id == 0 || hireling_id == 1); + if (bNoNPCHiring || current_screen_type != SCREEN_GAME) return; engine->_messageQueue->clear(); @@ -482,7 +484,7 @@ void Game::processQueuedMessages() { continue; case UIMSG_StartHireling1Dialogue: case UIMSG_StartHireling2Dialogue: - Game_StartHirelingDialogue(uMessage - UIMSG_StartHireling1Dialogue); + Game_StartHirelingDialogue(uMessage == UIMSG_StartHireling1Dialogue ? 0 : 1); continue; case UIMSG_HouseScreenClick: if (window_SpeakInHouse) { @@ -783,8 +785,8 @@ void Game::processQueuedMessages() { onEscape(); continue; case SCREEN_INPUT_BLV: // click escape - if (uCurrentHouse_Animation == 153) - playHouseSound((HOUSE_ID)0x99u, HouseSoundType(3)); // TODO(Nik-RE-dev): what is this? + if (uCurrentHouse_Animation == 153) // TODO(Nik-RE-dev): what is this? Btw, 153 == HOUSE_EARTH_GUILD_STONE_CITY. + playHouseSound(HOUSE_EARTH_GUILD_STONE_CITY, HOUSE_SOUND_MAGIC_GUILD_MEMBERS_ONLY); pMediaPlayer->Unload(); if (npcIdToDismissAfterDialogue) { pParty->hirelingScrollPosition = 0; diff --git a/src/Arcomage/Arcomage.cpp b/src/Arcomage/Arcomage.cpp index 67a3ad9a504e..332fde0e7bce 100644 --- a/src/Arcomage/Arcomage.cpp +++ b/src/Arcomage/Arcomage.cpp @@ -11,7 +11,6 @@ #include "Engine/Tables/AwardTable.h" #include "Engine/AssetsManager.h" -#include "GUI/GUIFont.h" #include "GUI/GUIWindow.h" #include "GUI/UI/UIHouses.h" @@ -20,6 +19,7 @@ #include "Library/Random/Random.h" +#include "Utility/IndexedArray.h" void SetStartConditions(); void SetStartGameData(); @@ -72,20 +72,21 @@ struct ArcomageStartConditions { int mastery_lvl; }; -const ArcomageStartConditions start_conditions[13] = { - {30, 100, 15, 5, 2, 2, 2, 10, 10, 10, 0}, - {50, 150, 20, 5, 2, 2, 2, 5, 5, 5, 1}, - {50, 150, 20, 5, 2, 2, 2, 5, 5, 5, 2}, - {75, 200, 25, 10, 3, 3, 3, 5, 5, 5, 2}, - {75, 200, 20, 10, 3, 3, 3, 5, 5, 5, 1}, - {100, 300, 30, 15, 4, 4, 4, 10, 10, 10, 1}, - {100, 300, 30, 15, 4, 4, 4, 10, 10, 10, 2}, - {150, 400, 20, 10, 5, 5, 5, 25, 25, 25, 0}, - {200, 500, 20, 10, 1, 1, 1, 15, 15, 15, 2}, - {100, 300, 20, 50, 1, 1, 5, 5, 5, 25, 0}, - {125, 350, 10, 20, 3, 1, 2, 15, 5, 10, 2}, - {125, 350, 10, 20, 3, 1, 2, 15, 5, 10, 1}, - {100, 300, 50, 50, 5, 3, 5, 20, 10, 20, 0}}; +IndexedArray start_conditions = { + {HOUSE_TAVERN_HARMONDALE, {30, 100, 15, 5, 2, 2, 2, 10, 10, 10, 0}}, + {HOUSE_TAVERN_ERATHIA, {50, 150, 20, 5, 2, 2, 2, 5, 5, 5, 1}}, + {HOUSE_TAVERN_TULAREAN_FOREST, {50, 150, 20, 5, 2, 2, 2, 5, 5, 5, 2}}, + {HOUSE_TAVERN_DEYJA, {75, 200, 25, 10, 3, 3, 3, 5, 5, 5, 2}}, + {HOUSE_TAVERN_BRACADA_DESERT, {75, 200, 20, 10, 3, 3, 3, 5, 5, 5, 1}}, + {HOUSE_TAVERN_CELESTE, {100, 300, 30, 15, 4, 4, 4, 10, 10, 10, 1}}, + {HOUSE_TAVERN_PIT, {100, 300, 30, 15, 4, 4, 4, 10, 10, 10, 2}}, + {HOUSE_TAVERN_EVENMORN_ISLAND, {150, 400, 20, 10, 5, 5, 5, 25, 25, 25, 0}}, + {HOUSE_TAVERN_MOUNT_NIGHON, {200, 500, 20, 10, 1, 1, 1, 15, 15, 15, 2}}, + {HOUSE_TAVERN_BARROW_DOWNS, {100, 300, 20, 50, 1, 1, 5, 5, 5, 25, 0}}, + {HOUSE_TAVERN_TATALIA, {125, 350, 10, 20, 3, 1, 2, 15, 5, 10, 2}}, + {HOUSE_TAVERN_AVLEE, {125, 350, 10, 20, 3, 1, 2, 15, 5, 10, 1}}, + {HOUSE_TAVERN_STONE_CITY, {100, 300, 50, 50, 5, 3, 5, 20, 10, 20, 0}} +}; constexpr auto SIG_MEMALOC = 0x67707274; // memory allocated; constexpr auto SIG_MEMFREE = 0x78787878; // memory free; @@ -571,7 +572,7 @@ bool ArcomageGame::LoadSprites() { int CalculateCardPower(ArcomagePlayer *player, ArcomagePlayer *enemy, ArcomageCard *pCard, int mastery) { - enum V_IND { + enum class V_IND { P_TOWER_M10, P_WALL_M10, E_TOWER, @@ -580,23 +581,23 @@ int CalculateCardPower(ArcomagePlayer *player, ArcomagePlayer *enemy, E_QUARRY, E_MAGIC, E_ZOO, - E_RES, - V_INDEX_MAX + E_RES }; + using enum V_IND; // mastery coeffs // base mastery focus on growing walls + tower // second level high priority on resource gen - const int mastery_coeff[V_INDEX_MAX][2] = { - {10, 5}, // P_TOWER_M10 - {2, 1}, // P_WALL_M10 - {1, 10}, // E_TOWER - {1, 3}, // E_WALL - {1, 7}, // E_BUILDINGS - {1, 5}, // E_QUARRY - {1, 40}, // E_MAGIC - {1, 40}, // E_ZOO - {1, 2} // E_RES + static constexpr IndexedArray, P_TOWER_M10, E_RES> mastery_coeff = { + {P_TOWER_M10, {{10, 5}}}, + {P_WALL_M10, {{2, 1}}}, + {E_TOWER, {{1, 10}}}, + {E_WALL, {{1, 3}}}, + {E_BUILDINGS, {{1, 7}}}, + {E_QUARRY, {{1, 5}}}, + {E_MAGIC, {{1, 40}}}, + {E_ZOO, {{1, 40}}}, + {E_RES, {{1, 2}}} }; int card_power = 0; @@ -2843,9 +2844,7 @@ void GameResultsApply() { pArcomageGame->Victory_type = victory_type; pArcomageGame->uGameWinner = winner; if (winner == 1) { - // 108..120 - tavern ids - // TODO: get rid of static cast, just place HOUSE_ID in proper place - HOUSE_ID houseId = static_cast(window_SpeakInHouse->wData.val); + HOUSE_ID houseId = window_SpeakInHouse->houseId(); if (isArcomageTavern(houseId)) { if (!pParty->pArcomageWins[houseId]) { pParty->pArcomageWins[houseId] = 1; @@ -2936,7 +2935,7 @@ void ArcomageGame::PrepareArcomage() { void SetStartConditions() { const ArcomageStartConditions *st_cond; - st_cond = &start_conditions[window_SpeakInHouse->wData.val - 108]; + st_cond = &start_conditions[window_SpeakInHouse->houseId()]; // set start conditions start_tower_height = st_cond->tower_height; diff --git a/src/Engine/Engine.cpp b/src/Engine/Engine.cpp index 9fbe41920dc5..729657c5dc3a 100644 --- a/src/Engine/Engine.cpp +++ b/src/Engine/Engine.cpp @@ -641,7 +641,8 @@ void DoPrepareWorld(bool bLoading, int _1_fullscreen_loading_2_box) { // spawning grounds & walls of mist - no loot & exp from monsters for (uint i = 0; i < pActors.size(); ++i) { - pActors[i].monsterInfo.uTreasureType = 0; + // TODO(captainurist): shouldn't we also set uTreasureLevel = ITEM_TREASURE_LEVEL_INVALID? + pActors[i].monsterInfo.uTreasureType = RANDOM_ITEM_ANY; pActors[i].monsterInfo.uTreasureDiceRolls = 0; pActors[i].monsterInfo.uExp = 0; } @@ -996,13 +997,13 @@ void Engine::_461103_load_level_sub() { //{ // v3 = pActors[i].pMonsterInfo.uID; v17 = 0; - if (pActors[i].monsterInfo.uID >= 115 && - pActors[i].monsterInfo.uID <= 186 || - pActors[i].monsterInfo.uID >= 232 && - pActors[i].monsterInfo.uID <= 249) + if (pActors[i].monsterInfo.uID >= MONSTER_115 && + pActors[i].monsterInfo.uID <= MONSTER_186 || + pActors[i].monsterInfo.uID >= MONSTER_232 && + pActors[i].monsterInfo.uID <= MONSTER_PEASANT_GOBLIN_MALE_3_3) v17 = 1; // v1 = 0; - v4 = (pActors[i].monsterInfo.uID - 1) % 3; + v4 = (std::to_underlying(pActors[i].monsterInfo.uID) - 1) % 3; // TODO(captainurist): encapsulate monster tier calculation. if (2 == v4) { if (pActors[i].npcId && pActors[i].npcId < 5000) continue; } else { @@ -1035,6 +1036,8 @@ void Engine::_461103_load_level_sub() { v20 = 0; // v16 = v1; + // TODO(captainurist): can drop this code? +#if 0 for (uint i = 0; i < pActors.size(); ++i) { // v7 = (char *)&pActors[0].pMonsterInfo; // do @@ -1053,6 +1056,7 @@ void Engine::_461103_load_level_sub() { //} // while ( v16 < (signed int)v5 ); } +#endif pGameLoadingUI_ProgressBar->Progress(); diff --git a/src/Engine/Events/EventIR.cpp b/src/Engine/Events/EventIR.cpp index 95a1266c714d..4492a0fc1a81 100644 --- a/src/Engine/Events/EventIR.cpp +++ b/src/Engine/Events/EventIR.cpp @@ -771,7 +771,7 @@ std::string EventIR::toString() const { case EVENT_MoveNPC: return fmt::format("{}: MoveNPC({}, {})", step, data.npc_move_descr.npc_id, std::to_underlying(data.npc_move_descr.location_id)); case EVENT_GiveItem: - return fmt::format("{}: GiveItem({}, {}, {})", step, std::to_underlying(data.give_item_descr.treasure_level), data.give_item_descr.treasure_type, std::to_underlying(data.give_item_descr.item_id)); + return fmt::format("{}: GiveItem({}, {}, {})", step, std::to_underlying(data.give_item_descr.treasure_level), std::to_underlying(data.give_item_descr.treasure_type), std::to_underlying(data.give_item_descr.item_id)); case EVENT_ChangeEvent: return fmt::format("{}: ChangeEvent({})", step, data.event_id); case EVENT_CheckSkill: @@ -865,6 +865,8 @@ EventIR EventIR::parse(const RawEvent *evt, size_t size) { return std::string(reinterpret_cast(ptr), length); }; + // TODO(captainurist): verify enum ranges here. + switch (ir.type) { case EVENT_Exit: break; @@ -1074,8 +1076,8 @@ EventIR EventIR::parse(const RawEvent *evt, size_t size) { break; case EVENT_GiveItem: requireSize(11); - ir.data.give_item_descr.treasure_level = (ItemTreasureLevel)evt->v5; - ir.data.give_item_descr.treasure_type = evt->v6; + ir.data.give_item_descr.treasure_level = static_cast(evt->v5); + ir.data.give_item_descr.treasure_type = static_cast(evt->v6); ir.data.give_item_descr.item_id = static_cast(EVT_DWORD(&evt->v7)); break; case EVENT_ChangeEvent: diff --git a/src/Engine/Events/EventIR.h b/src/Engine/Events/EventIR.h index f51a60927a22..6ed293e5d315 100644 --- a/src/Engine/Events/EventIR.h +++ b/src/Engine/Events/EventIR.h @@ -157,7 +157,7 @@ class EventIR { } npc_groups_descr; struct { ItemTreasureLevel treasure_level; - int treasure_type; + RandomItemType treasure_type; ItemId item_id; } give_item_descr; struct { diff --git a/src/Engine/Graphics/CMakeLists.txt b/src/Engine/Graphics/CMakeLists.txt index 78c285523074..57fffacf30be 100644 --- a/src/Engine/Graphics/CMakeLists.txt +++ b/src/Engine/Graphics/CMakeLists.txt @@ -57,7 +57,6 @@ set(ENGINE_GRAPHICS_HEADERS Level/Decoration.h LightmapBuilder.h LightsStack.h - LocationEnums.h LocationFunctions.h LocationInfo.h LocationTime.h diff --git a/src/Engine/Graphics/Collisions.cpp b/src/Engine/Graphics/Collisions.cpp index b9071f47d7be..991d66075732 100644 --- a/src/Engine/Graphics/Collisions.cpp +++ b/src/Engine/Graphics/Collisions.cpp @@ -545,7 +545,7 @@ void ProcessActorCollisionsBLV(Actor &actor, bool isAboveGround, bool isFlying) } if (type == OBJECT_Character) { - if (actor.GetActorsRelation(0)) { + if (actor.GetActorsRelation(0) != MonsterInfo::Hostility_Friendly) { actor.speed.y = 0; actor.speed.x = 0; @@ -667,7 +667,7 @@ void ProcessActorCollisionsODM(Actor &actor, bool isFlying) { } if (type == OBJECT_Character) { - if (actor.GetActorsRelation(0)) { + if (actor.GetActorsRelation(0) != MonsterInfo::Hostility_Friendly) { actor.speed.y = 0; actor.speed.x = 0; diff --git a/src/Engine/Graphics/Indoor.cpp b/src/Engine/Graphics/Indoor.cpp index 6c2161f895b4..d5becc91b7b3 100644 --- a/src/Engine/Graphics/Indoor.cpp +++ b/src/Engine/Graphics/Indoor.cpp @@ -1021,7 +1021,7 @@ void PrepareToLoadBLV(bool bLoading) { // Party to start position Actor this_; - this_.monsterInfo.uID = 45; + this_.monsterInfo.uID = MONSTER_45; this_.PrepareSprites(0); if (!bLoading) { pParty->_viewPitch = 0; @@ -1987,7 +1987,7 @@ int SpawnEncounterMonsters(MapInfo *map_info, int enc_index) { } //----- (00450521) -------------------------------------------------------- -int DropTreasureAt(ItemTreasureLevel trs_level, int trs_type, Vec3i pos, uint16_t facing) { +int DropTreasureAt(ItemTreasureLevel trs_level, RandomItemType trs_type, Vec3i pos, uint16_t facing) { SpriteObject a1; pItemTable->generateItem(trs_level, trs_type, &a1.containing_item); a1.uType = pItemTable->pItems[a1.containing_item.uItemID].uSpriteID; @@ -2009,7 +2009,7 @@ void SpawnRandomTreasure(MapInfo *mapInfo, SpawnPoint *a2) { int v34 = 0; int v5 = grng->random(100); ItemTreasureLevel v13 = grng->randomSample(RemapTreasureLevel(a2->uItemIndex, mapInfo->Treasure_prob)); - if (v13 != ITEM_TREASURE_LEVEL_GUARANTEED_ARTIFACT) { + if (v13 != ITEM_TREASURE_LEVEL_7) { // [0, 20) -- nothing // [20, 60) -- gold // [60, 100) -- item @@ -2018,7 +2018,7 @@ void SpawnRandomTreasure(MapInfo *mapInfo, SpawnPoint *a2) { return; if (v5 >= 60) { - DropTreasureAt(v13, grng->random(27) + 20, a2->vPosition, 0); + DropTreasureAt(v13, grng->randomSample(allSpawnableRandomItemTypes()), a2->vPosition, 0); return; } diff --git a/src/Engine/Graphics/Indoor.h b/src/Engine/Graphics/Indoor.h index b33b8428db35..be5500f794e2 100644 --- a/src/Engine/Graphics/Indoor.h +++ b/src/Engine/Graphics/Indoor.h @@ -12,7 +12,6 @@ #include "BSPModel.h" #include "LocationInfo.h" #include "LocationTime.h" -#include "LocationEnums.h" #include "LocationFunctions.h" struct IndoorLocation; @@ -317,7 +316,7 @@ int CalcDistPointToLine(int a1, int a2, int a3, int a4, int a5, int a6); void PrepareDrawLists_BLV(); void PrepareToLoadBLV(bool bLoading); int SpawnEncounterMonsters(struct MapInfo *a1, int a2); -int DropTreasureAt(ItemTreasureLevel trs_level, signed int trs_type, Vec3i pos, uint16_t facing); +int DropTreasureAt(ItemTreasureLevel trs_level, RandomItemType trs_type, Vec3i pos, uint16_t facing); void SpawnRandomTreasure(MapInfo *mapInfo, SpawnPoint *a2); void FindBillboardsLightLevels_BLV(); diff --git a/src/Engine/Graphics/LocationEnums.h b/src/Engine/Graphics/LocationEnums.h deleted file mode 100644 index 27c132bbf36e..000000000000 --- a/src/Engine/Graphics/LocationEnums.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -// TODO(captainurist): merge with MapEnums.h - -// TODO(captainurist): enum? -#define DAY_ATTRIB_FOG 1 - -enum class LevelType { - LEVEL_NULL = 0, - LEVEL_INDOOR = 0x1, - LEVEL_OUTDOOR = 0x2, -}; -using enum LevelType; - -enum { - MODEL_INDOOR = -1 -}; diff --git a/src/Engine/Graphics/LocationFunctions.h b/src/Engine/Graphics/LocationFunctions.h index c8e3966e06ea..a43b778e8211 100644 --- a/src/Engine/Graphics/LocationFunctions.h +++ b/src/Engine/Graphics/LocationFunctions.h @@ -1,9 +1,12 @@ #pragma once -#include "LocationEnums.h" +#include "Engine/MapEnums.h" + #include "LocationInfo.h" #include "LocationTime.h" +// TODO(captainurist): move to Engine/ and drop the Location- prefix, should be MapSmth. + extern LevelType uCurrentlyLoadedLevelType; bool GetAlertStatus(); diff --git a/src/Engine/Graphics/LocationInfo.h b/src/Engine/Graphics/LocationInfo.h index 20a20b27de4b..1a7ab2afd1e8 100644 --- a/src/Engine/Graphics/LocationInfo.h +++ b/src/Engine/Graphics/LocationInfo.h @@ -1,5 +1,6 @@ #pragma once +// TODO(captainurist): we also have MapInfo, rename this into something more sane with Map- prefix. struct LocationInfo { int respawnCount = 0; // Number of times a location was respawned, including the initial spawn. int lastRespawnDay = 0; // Day of the last respawn (days since GameTime zero to last respawn). diff --git a/src/Engine/Graphics/LocationTime.h b/src/Engine/Graphics/LocationTime.h index c4d91324f860..4373164bb64b 100644 --- a/src/Engine/Graphics/LocationTime.h +++ b/src/Engine/Graphics/LocationTime.h @@ -3,11 +3,13 @@ #include #include "Engine/Time.h" +#include "Engine/MapEnums.h" +// TODO(captainurist): rename to smth like MapTime. struct LocationTime { GameTime last_visit; std::string sky_texture_name; - int day_attrib = 0; // TODO(captainurist): actually WeatherFlags, see DAY_ATTRIB_FOG. + MapWeatherFlags day_attrib = 0; int day_fogrange_1 = 0; int day_fogrange_2 = 0; }; diff --git a/src/Engine/Graphics/Outdoor.cpp b/src/Engine/Graphics/Outdoor.cpp index 872aac7f5edc..dc3ab3e02b39 100644 --- a/src/Engine/Graphics/Outdoor.cpp +++ b/src/Engine/Graphics/Outdoor.cpp @@ -479,13 +479,13 @@ void OutdoorLocation::SetFog() { if (chance < fog_probability_table[map_id].small_fog_chance) { ::day_fogrange_1 = 4096; ::day_fogrange_2 = 8192; - ::day_attrib |= DAY_ATTRIB_FOG; + ::day_attrib |= MAP_WEATHER_FOGGY; } else if (chance < fog_probability_table[map_id].small_fog_chance + fog_probability_table[map_id].average_fog_chance) { ::day_fogrange_2 = 4096; ::day_fogrange_1 = 0; - ::day_attrib |= DAY_ATTRIB_FOG; + ::day_attrib |= MAP_WEATHER_FOGGY; } else if (fog_probability_table[map_id].dense_fog_chance && chance < fog_probability_table[map_id].small_fog_chance + @@ -493,9 +493,9 @@ void OutdoorLocation::SetFog() { fog_probability_table[map_id].dense_fog_chance) { ::day_fogrange_2 = 2048; ::day_fogrange_1 = 0; - ::day_attrib |= DAY_ATTRIB_FOG; + ::day_attrib |= MAP_WEATHER_FOGGY; } else { - ::day_attrib &= ~DAY_ATTRIB_FOG; + ::day_attrib &= ~MAP_WEATHER_FOGGY; } if (Is_out15odm_underwater()) SetUnderwaterFog(); @@ -1430,7 +1430,7 @@ void OutdoorLocation::PrepareActorsDrawList() { pBillboardRenderList[uNumBillboardsToDraw - 1].field_1E = flags | 0x200; pBillboardRenderList[uNumBillboardsToDraw - 1].pSpriteFrame = frame; pBillboardRenderList[uNumBillboardsToDraw - 1].sTintColor = - pMonsterList->pMonsters[pActors[i].monsterInfo.uID - 1].sTintColor; // *((int *)&v35[v36] - 36); + pMonsterList->pMonsters[pActors[i].monsterInfo.uID].sTintColor; // *((int *)&v35[v36] - 36); if (pActors[i].buffs[ACTOR_BUFF_STONED].Active()) { pBillboardRenderList[uNumBillboardsToDraw - 1].field_1E = flags | 0x100; @@ -2680,7 +2680,7 @@ void ODM_LoadAndInitialize(const std::string &pFilename, ODMRenderParams *thisa) map_info = &pMapStats->pInfos[map_id]; respawn_interval = map_info->uRespawnIntervalDays; } - day_attrib &= ~DAY_ATTRIB_FOG; + day_attrib &= ~MAP_WEATHER_FOGGY; dword_6BE13C_uCurrentlyLoadedLocationID = map_id; bool outdoor_was_respawned; pOutdoor->Initialize(pFilename, pParty->GetPlayingTime().GetDays() + 1, @@ -2728,7 +2728,7 @@ Color GetLevelFogColor() { return colorTable.OliveDrab; } - if (day_attrib & DAY_ATTRIB_FOG) { + if (day_attrib & MAP_WEATHER_FOGGY) { if (pWeather->bNight) { // night-time fog if (false) { logger->error("decompilation can be inaccurate, please send savegame to Nomad"); @@ -2760,7 +2760,7 @@ int sub_47C3D7_get_fog_specular(int unused, int isSky, float screen_depth) { if (engine->IsUnderwater()) isNight = false; if (pParty->armageddon_timer || - !(day_attrib & DAY_ATTRIB_FOG) && !engine->IsUnderwater()) + !(day_attrib & MAP_WEATHER_FOGGY) && !engine->IsUnderwater()) return 0xFF000000; if (isNight) { if (screen_depth < (double)day_fogrange_1) { diff --git a/src/Engine/Graphics/Outdoor.h b/src/Engine/Graphics/Outdoor.h index 92e802ad153b..535bc1beddf2 100644 --- a/src/Engine/Graphics/Outdoor.h +++ b/src/Engine/Graphics/Outdoor.h @@ -15,7 +15,6 @@ #include "BSPModel.h" #include "LocationInfo.h" #include "LocationTime.h" -#include "LocationEnums.h" #include "LocationFunctions.h" class Logger; diff --git a/src/Engine/Graphics/Viewport.cpp b/src/Engine/Graphics/Viewport.cpp index 6a6bee92713a..932fe6b62c03 100644 --- a/src/Engine/Graphics/Viewport.cpp +++ b/src/Engine/Graphics/Viewport.cpp @@ -246,7 +246,7 @@ void ItemInteraction(unsigned int item_id) { } bool CanInteractWithActor(unsigned int id) { - return !pActors[id].GetActorsRelation(0) && pActors[id].ActorFriend() && pActors[id].CanAct(); + return pActors[id].GetActorsRelation(0) == MonsterInfo::Hostility_Friendly && pActors[id].ActorFriend() && pActors[id].CanAct(); } void InteractWithActor(unsigned int id) { diff --git a/src/Engine/Graphics/Vis.cpp b/src/Engine/Graphics/Vis.cpp index c93834e12b6d..c6c7087782fe 100644 --- a/src/Engine/Graphics/Vis.cpp +++ b/src/Engine/Graphics/Vis.cpp @@ -800,7 +800,7 @@ bool Vis::isBillboardPartOfSelection(int billboardId, Vis_SelectionFilter *filte return true; auto relation = pActors[object_idx].GetActorsRelation(nullptr); - if (relation == 0) return false; + if (relation == MonsterInfo::Hostility_Friendly) return false; return true; } diff --git a/src/Engine/MM7.h b/src/Engine/MM7.h index d6d3b44da695..3315b1ea7773 100644 --- a/src/Engine/MM7.h +++ b/src/Engine/MM7.h @@ -7,7 +7,8 @@ typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned long ulong; -/* 297 */ +// TODO(captainurist): #enum class +// TODO(captainurist): this duplicates enum SoundID. enum SoundType { SOUND_EndTurnBasedMode = 0xCE, SOUND_StartTurnBasedMode = 0xCF, diff --git a/src/Engine/MapEnums.h b/src/Engine/MapEnums.h index a12d0ee9302c..d675b3a0e0fd 100644 --- a/src/Engine/MapEnums.h +++ b/src/Engine/MapEnums.h @@ -4,6 +4,31 @@ #include "Library/Serialization/SerializationFwd.h" +#include "Utility/Flags.h" + +/** + * MM7 has only one weather flag, but apparently we have more in MM8. + * + * @see https://github.com/GrayFace/MMExtension/blob/4d6600f164315f38157591d7f0307a86594c22ef/Scripts/Structs/01%20common%20structs.lua#L412C8-L412C15 + */ +enum class MapWeatherFlag { + MAP_WEATHER_FOGGY = 1 +}; +using enum MapWeatherFlag; +MM_DECLARE_FLAGS(MapWeatherFlags, MapWeatherFlag) +MM_DECLARE_OPERATORS_FOR_FLAGS(MapWeatherFlags) + +enum class LevelType { + LEVEL_NULL = 0, + LEVEL_INDOOR = 0x1, + LEVEL_OUTDOOR = 0x2, +}; +using enum LevelType; + +enum { + MODEL_INDOOR = -1 +}; + /** * Enum of all maps in the game. * @@ -110,9 +135,6 @@ enum class MAP_TREASURE_LEVEL : int8_t { MAP_TREASURE_LEVEL_5 = 4, MAP_TREASURE_LEVEL_6 = 5, MAP_TREASURE_LEVEL_7 = 6, - - MAP_TREASURE_LEVEL_FIRST = MAP_TREASURE_LEVEL_1, - MAP_TREASURE_LEVEL_LAST = MAP_TREASURE_LEVEL_7 }; using enum MAP_TREASURE_LEVEL; diff --git a/src/Engine/Objects/Actor.cpp b/src/Engine/Objects/Actor.cpp index 4550b6e52529..bdbcfbf3bdfe 100644 --- a/src/Engine/Objects/Actor.cpp +++ b/src/Engine/Objects/Actor.cpp @@ -51,7 +51,14 @@ std::vector pActors; stru319 stru_50C198; // idb -std::array _4DF380_hostilityRanges = {0, 1024, 2560, 5120, 10240}; +// TODO(captainurist): HOSTILITY_FIRST, HOSTILITY_LAST +static constexpr IndexedArray _4DF380_hostilityRanges = { + {MonsterInfo::Hostility_Friendly, 0}, + {MonsterInfo::Hostility_Close, 1024}, + {MonsterInfo::Hostility_Short, 2560}, + {MonsterInfo::Hostility_Medium, 5120}, + {MonsterInfo::Hostility_Long, 10240} +}; std::array word_4E8152 = {{0, 0, 0, 90, 8, 2, 70, 20, 10, 50, 30}}; // level spawn monster levels ABC @@ -690,11 +697,12 @@ unsigned short Actor::GetObjDescId(SpellId spellId) { } bool Actor::ArePeasantsOfSameFaction(Actor *a1, Actor *a2) { + // TODO(captainurist): encapsulate enum arithmetic. unsigned int v2 = a1->ally; - if (!a1->ally) v2 = (a1->monsterInfo.uID - 1) / 3 + 1; + if (!a1->ally) v2 = (std::to_underlying(a1->monsterInfo.uID) - 1) / 3 + 1; unsigned int v3 = a2->ally; - if (!a2->ally) v3 = (a2->monsterInfo.uID - 1) / 3 + 1; + if (!a2->ally) v3 = (std::to_underlying(a2->monsterInfo.uID) - 1) / 3 + 1; if (v2 >= 39 && v2 <= 44 && v3 >= 39 && v3 <= 44 || v2 >= 45 && v2 <= 50 && v3 >= 45 && v3 <= 50 || @@ -817,7 +825,7 @@ void Actor::AI_RangedAttack(unsigned int uActorID, struct AIDirection *pDir, a1.Create(pDir->uYawAngle, pDir->uPitchAngle, pObjectList->pObjects[(int16_t)a1.uObjectDescID].uSpeed, 0); - if (pActors[uActorID].monsterInfo.uSpecialAbilityType == 1) { + if (pActors[uActorID].monsterInfo.uSpecialAbilityType == MONSTER_SPECIAL_ABILITY_SHOT) { specAb = pActors[uActorID].monsterInfo.uSpecialAbilityDamageDiceBonus; if (specAb == 2) { a1.vPosition.z += 40; @@ -1272,7 +1280,8 @@ bool Actor::IsPeasant() { unsigned int InHostile_Id; // eax@1 InHostile_Id = this->ally; - if (!this->ally) InHostile_Id = (this->monsterInfo.uID - 1) / 3 + 1; + // TODO(captainurist): encapsulate enum arithmetic. + if (!this->ally) InHostile_Id = (std::to_underlying(this->monsterInfo.uID) - 1) / 3 + 1; return (signed int)InHostile_Id >= 39 && (signed int)InHostile_Id <= 44 // Dwarfs peasants || (signed int)InHostile_Id >= 45 && @@ -1724,7 +1733,7 @@ void Actor::AI_Stun(unsigned int uActorID, Pid edx0, if (pActors[uActorID].aiState == Fleeing) pActors[uActorID].attributes |= ACTOR_FLEEING; - if (pActors[uActorID].monsterInfo.uHostilityType != 4) { + if (pActors[uActorID].monsterInfo.uHostilityType != MonsterInfo::Hostility_Long) { pActors[uActorID].attributes &= ~ACTOR_UNKNOWN_4; pActors[uActorID].monsterInfo.uHostilityType = MonsterInfo::Hostility_Long; } @@ -1811,7 +1820,7 @@ void Actor::resurrect(unsigned int uActorID) { pActor->monsterInfo.uTreasureDiceRolls = 0; pActor->monsterInfo.uTreasureDiceSides = 0; pActor->monsterInfo.uTreasureLevel = ITEM_TREASURE_LEVEL_INVALID; - pActor->monsterInfo.uTreasureType = 0; + pActor->monsterInfo.uTreasureType = RANDOM_ITEM_ANY; pActor->ally = 9999; pActor->ResetAggressor(); // ~0x80000 pActor->group = 0; @@ -2135,10 +2144,10 @@ void Actor::AI_Pursue3(unsigned int uActorID, Pid a2, void Actor::_SelectTarget(unsigned int uActorID, Pid *OutTargetPID, bool can_target_party) { int v5; // ecx@1 - signed int v10; // eax@13 + MonsterInfo::HostilityRadius v10; // eax@13 uint v11; // ebx@16 uint v12; // eax@16 - signed int v14; // eax@31 + MonsterInfo::HostilityRadius v14; // eax@31 uint v15; // edi@43 //uint v16; // ebx@45 //uint v17; // eax@45 @@ -2165,18 +2174,18 @@ void Actor::_SelectTarget(unsigned int uActorID, Pid *OutTargetPID, if (!thisActor->lastCharacterIdToHit || Pid(OBJECT_Actor, v5) != thisActor->lastCharacterIdToHit) { v10 = thisActor->GetActorsRelation(actor); - if (v10 == 0) continue; + if (v10 == MonsterInfo::Hostility_Friendly) continue; } else if (thisActor->IsNotAlive()) { thisActor->lastCharacterIdToHit = Pid(); v10 = thisActor->GetActorsRelation(actor); - if (v10 == 0) continue; + if (v10 == MonsterInfo::Hostility_Friendly) continue; } else { if ((actor->group != 0 || thisActor->group != 0) && actor->group == thisActor->group) continue; - v10 = 4; + v10 = MonsterInfo::Hostility_Long; } - if (thisActor->monsterInfo.uHostilityType) + if (thisActor->monsterInfo.uHostilityType != MonsterInfo::Hostility_Friendly) v10 = pMonsterStats->pInfos[thisActor->monsterInfo.uID] .uHostilityType; v11 = _4DF380_hostilityRanges[v10]; @@ -2201,14 +2210,14 @@ void Actor::_SelectTarget(unsigned int uActorID, Pid *OutTargetPID, !thisActor->buffs[ACTOR_BUFF_ENSLAVED].Active() && !thisActor->buffs[ACTOR_BUFF_CHARM].Active() && !thisActor->buffs[ACTOR_BUFF_SUMMONED].Active()) - v14 = 4; + v14 = MonsterInfo::Hostility_Long; else v14 = thisActor->GetActorsRelation(0); - if (v14 != 0) { - if (!thisActor->monsterInfo.uHostilityType) + if (v14 != MonsterInfo::Hostility_Friendly) { + if (thisActor->monsterInfo.uHostilityType == MonsterInfo::Hostility_Friendly) v15 = _4DF380_hostilityRanges[v14]; else - v15 = _4DF380_hostilityRanges[4]; + v15 = _4DF380_hostilityRanges[MonsterInfo::Hostility_Long]; uint v16 = std::abs(thisActor->pos.x - pParty->pos.x); uint v28 = std::abs(thisActor->pos.y - pParty->pos.y); uint v17 = std::abs(thisActor->pos.z - pParty->pos.z); @@ -2221,7 +2230,7 @@ void Actor::_SelectTarget(unsigned int uActorID, Pid *OutTargetPID, } //----- (0040104C) -------------------------------------------------------- -signed int Actor::GetActorsRelation(Actor *otherActPtr) { +MonsterInfo::HostilityRadius Actor::GetActorsRelation(Actor *otherActPtr) { unsigned int thisGroup; // ebp@19 int otherGroup; // eax@22 unsigned int thisAlly; // edx@25 @@ -2230,20 +2239,20 @@ signed int Actor::GetActorsRelation(Actor *otherActPtr) { if (otherActPtr) { if (otherActPtr->group != 0 && this->group != 0 && otherActPtr->group == this->group) - return 0; + return MonsterInfo::Hostility_Friendly; } - if (this->buffs[ACTOR_BUFF_BERSERK].Active()) return 4; + if (this->buffs[ACTOR_BUFF_BERSERK].Active()) return MonsterInfo::Hostility_Long; thisAlly = this->ally; if (this->buffs[ACTOR_BUFF_ENSLAVED].Active() || thisAlly == 9999) thisGroup = 0; else if (thisAlly > 0) thisGroup = thisAlly; else - thisGroup = (this->monsterInfo.uID - 1) / 3 + 1; + thisGroup = (std::to_underlying(this->monsterInfo.uID) - 1) / 3 + 1; // TODO(captainurist): encapsulate enum arithmetic. if (otherActPtr) { - if (otherActPtr->buffs[ACTOR_BUFF_BERSERK].Active()) return 4; + if (otherActPtr->buffs[ACTOR_BUFF_BERSERK].Active()) return MonsterInfo::Hostility_Long; otherAlly = otherActPtr->ally; if (otherActPtr->buffs[ACTOR_BUFF_ENSLAVED].Active() || otherAlly == 9999) @@ -2251,7 +2260,7 @@ signed int Actor::GetActorsRelation(Actor *otherActPtr) { else if (otherAlly > 0) otherGroup = otherAlly; else - otherGroup = (otherActPtr->monsterInfo.uID - 1) / 3 + 1; + otherGroup = (std::to_underlying(otherActPtr->monsterInfo.uID) - 1) / 3 + 1; // TODO(captainurist): encapsulate enum arithmetic. } else { otherGroup = 0; } @@ -2259,19 +2268,19 @@ signed int Actor::GetActorsRelation(Actor *otherActPtr) { if (this->buffs[ACTOR_BUFF_CHARM].Active() && !otherGroup || otherActPtr && otherActPtr->buffs[ACTOR_BUFF_CHARM].Active() && !thisGroup) - return 0; + return MonsterInfo::Hostility_Friendly; if (!this->buffs[ACTOR_BUFF_ENSLAVED].Active() && this->ActorEnemy() && !otherGroup) - return 4; - if (thisGroup >= 89 || otherGroup >= 89) return 0; + return MonsterInfo::Hostility_Long; + if (thisGroup >= 89 || otherGroup >= 89) return MonsterInfo::Hostility_Friendly; if (thisGroup == 0) { if ((!otherActPtr || this->buffs[ACTOR_BUFF_ENSLAVED].Active() && otherActPtr->ActorFriend()) && - !pFactionTable->relations[otherGroup][0]) + pFactionTable->relations[otherGroup][0] == MonsterInfo::Hostility_Friendly) return pFactionTable->relations[0][otherGroup]; else - return 4; + return MonsterInfo::Hostility_Long; } else { return pFactionTable->relations[thisGroup][otherGroup]; } @@ -2355,7 +2364,7 @@ void Actor::PrepareSprites(char load_sounds_if_bit1_set) { MonsterDesc *v3; // esi@1 MonsterInfo *v9; // [sp+84h] [bp-10h]@1 - v3 = &pMonsterList->pMonsters[monsterInfo.uID - 1]; + v3 = &pMonsterList->pMonsters[monsterInfo.uID]; v9 = &pMonsterStats->pInfos[monsterInfo.uID /*- 1 + 1*/]; // v12 = pSpriteIDs; // Source = (char *)v3->pSpriteNames; @@ -2477,7 +2486,7 @@ void Actor::ActorDamageFromMonster(Pid attacker_id, //----- (0044FD29) -------------------------------------------------------- void Actor::SummonMinion(int summonerId) { uint8_t extraSummonLevel; // al@1 - int summonMonsterBaseType; // esi@1 + MONSTER_TYPE summonMonsterBaseType; // esi@1 int v5; // edx@2 int v7; // edi@10 MonsterInfo *v9; // ebx@10 @@ -2496,8 +2505,8 @@ void Actor::SummonMinion(int summonerId) { v19 = this->ally; if (!this->ally) { - monsterId = this->monsterInfo.uID - 1; - v19 = (uint)(monsterId * 0.33333334); + monsterId = std::to_underlying(this->monsterInfo.uID) - 1; + v19 = monsterId / 3; // TODO(captainurist): forgetting + 1 here? } v27 = uCurrentlyLoadedLevelType == LEVEL_OUTDOOR ? 128 : 64; v13 = grng->random(2048); @@ -2513,34 +2522,36 @@ void Actor::SummonMinion(int summonerId) { } extraSummonLevel = this->monsterInfo.uSpecialAbilityDamageDiceRolls; - summonMonsterBaseType = this->monsterInfo.field_3C_some_special_attack; + // TODO(captainurist): drop the cast here, store the data properly. + summonMonsterBaseType = static_cast(this->monsterInfo.field_3C_some_special_attack); if (extraSummonLevel) { - if (extraSummonLevel >= 1 && extraSummonLevel <= 3) - summonMonsterBaseType = - summonMonsterBaseType + extraSummonLevel - 1; + if (extraSummonLevel >= 1 && extraSummonLevel <= 3) { + // TODO(captainurist): encapsulate monster level arithmetic properly. + summonMonsterBaseType = static_cast(std::to_underlying(summonMonsterBaseType) + extraSummonLevel - 1); + } } else { v5 = grng->random(100); + // TODO(captainurist): encapsulate monster level arithmetic properly. if (v5 >= 90) - summonMonsterBaseType += 2; + summonMonsterBaseType = static_cast(std::to_underlying(summonMonsterBaseType) + 2); else if (v5 >= 60) - summonMonsterBaseType += 1; + summonMonsterBaseType = static_cast(std::to_underlying(summonMonsterBaseType) + 1); } - v7 = summonMonsterBaseType - 1; Actor *actor = AllocateActor(true); if (!actor) return; - v9 = &pMonsterStats->pInfos[v7 + 1]; + v9 = &pMonsterStats->pInfos[summonMonsterBaseType]; actor->name = v9->pName; actor->currentHP = v9->uHP; actor->monsterInfo = *v9; actor->word_000086_some_monster_id = summonMonsterBaseType; - actor->radius = pMonsterList->pMonsters[v7].uMonsterRadius; - actor->height = pMonsterList->pMonsters[v7].uMonsterHeight; + actor->radius = pMonsterList->pMonsters[summonMonsterBaseType].uMonsterRadius; + actor->height = pMonsterList->pMonsters[summonMonsterBaseType].uMonsterHeight; actor->monsterInfo.uTreasureDiceRolls = 0; - actor->monsterInfo.uTreasureType = 0; + actor->monsterInfo.uTreasureType = RANDOM_ITEM_ANY; actor->monsterInfo.uExp = 0; - actor->moveSpeed = pMonsterList->pMonsters[v7].uMovementSpeed; + actor->moveSpeed = pMonsterList->pMonsters[summonMonsterBaseType].uMovementSpeed; actor->initialPosition.x = v15; actor->initialPosition.y = v17; @@ -2620,7 +2631,7 @@ void Actor::UpdateActorAI() { // If shrink expired: reset height if (pActor->buffs[ACTOR_BUFF_SHRINK].Expired()) { - pActor->height = pMonsterList->pMonsters[pActor->monsterInfo.uID - 1].uMonsterHeight; + pActor->height = pMonsterList->pMonsters[pActor->monsterInfo.uID].uMonsterHeight; pActor->buffs[ACTOR_BUFF_SHRINK].Reset(); } @@ -2676,7 +2687,7 @@ void Actor::UpdateActorAI() { Actor::_SelectTarget(actor_id, &ai_near_actors_targets_pid[actor_id], true); - if (pActor->monsterInfo.uHostilityType && !ai_near_actors_targets_pid[actor_id]) + if (pActor->monsterInfo.uHostilityType != MonsterInfo::Hostility_Friendly && !ai_near_actors_targets_pid[actor_id]) pActor->monsterInfo.uHostilityType = MonsterInfo::Hostility_Friendly; target_pid = ai_near_actors_targets_pid[actor_id]; @@ -2697,7 +2708,7 @@ void Actor::UpdateActorAI() { pActor->buffs[i].IsBuffExpiredToTime(pParty->GetPlayingTime()); if (pActor->buffs[ACTOR_BUFF_SHRINK].Expired()) { - pActor->height = pMonsterList->pMonsters[pActor->monsterInfo.uID - 1].uMonsterHeight; + pActor->height = pMonsterList->pMonsters[pActor->monsterInfo.uID].uMonsterHeight; pActor->buffs[ACTOR_BUFF_SHRINK].Reset(); } @@ -2757,21 +2768,23 @@ void Actor::UpdateActorAI() { int distanceToTarget = pDir->uDistance; - int relationToTarget; + MonsterInfo::HostilityRadius relationToTarget; if (pActor->monsterInfo.uHostilityType == MonsterInfo::Hostility_Friendly) { if (target_pid_type == OBJECT_Actor) { - relationToTarget = pFactionTable->relations[(pActor->monsterInfo.uID - 1) / 3 + 1][(pActors[target_pid.id()].monsterInfo.uID - 1) / 3 + 1]; + // TODO(captainurist): encapsulate enum arithmetic. + relationToTarget = pFactionTable->relations[(std::to_underlying(pActor->monsterInfo.uID) - 1) / 3 + 1] + [(std::to_underlying(pActors[target_pid.id()].monsterInfo.uID) - 1) / 3 + 1]; } else { - relationToTarget = 4; + relationToTarget = MonsterInfo::Hostility_Long; } v38 = 0; - if (relationToTarget == 2) + if (relationToTarget == MonsterInfo::Hostility_Short) v38 = 1024; - else if (relationToTarget == 3) + else if (relationToTarget == MonsterInfo::Hostility_Medium) v38 = 2560; - else if (relationToTarget == 4) + else if (relationToTarget == MonsterInfo::Hostility_Long) v38 = 5120; - if (relationToTarget >= 1 && relationToTarget <= 4 && distanceToTarget < v38 || relationToTarget == 1) + if (relationToTarget >= MonsterInfo::Hostility_Close && relationToTarget <= MonsterInfo::Hostility_Long && distanceToTarget < v38 || relationToTarget == MonsterInfo::Hostility_Close) pActor->monsterInfo.uHostilityType = MonsterInfo::Hostility_Long; } @@ -2941,7 +2954,7 @@ bool Actor::isActorKilled(ACTOR_KILL_CHECK_POLICY policy, int param, int count) deadActors = Actor::searchDeadActorsByGroup(&totalActors, param); break; case KILL_CHECK_MONSTERID: - deadActors = Actor::searchDeadActorsByMonsterID(&totalActors, param); + deadActors = Actor::searchDeadActorsByMonsterID(&totalActors, static_cast(param)); break; case KILL_CHECK_ACTORID: deadActors = Actor::searchDeadActorsByID(&totalActors, param); @@ -2985,7 +2998,7 @@ int Actor::searchDeadActorsByGroup(int *pTotalActors, int group) { return result; } -int Actor::searchDeadActorsByMonsterID(int *pTotalActors, int monsterID) { +int Actor::searchDeadActorsByMonsterID(int *pTotalActors, MONSTER_TYPE monsterID) { int result = 0, totalActors = 0; bool alert = GetAlertStatus(); @@ -3156,7 +3169,7 @@ void Actor::DamageMonsterFromParty(Pid a1, unsigned int uActorID_Monster, if (character->getSkillValue(CHARACTER_SKILL_BLASTER).mastery() >= CHARACTER_SKILL_MASTERY_MASTER) skillLevel = character->getSkillValue(CHARACTER_SKILL_BLASTER).level(); attackElement = DAMAGE_PHYSICAL; - uDamageAmount = character->CalculateMeleeDamageTo(true, true, 0); + uDamageAmount = character->CalculateMeleeDamageTo(true, true, MONSTER_0); if (!character->characterHitOrMiss(pMonster, v61, skillLevel)) { character->playReaction(SPEECH_ATTACK_MISS); return; @@ -3164,7 +3177,7 @@ void Actor::DamageMonsterFromParty(Pid a1, unsigned int uActorID_Monster, break; case SPELL_101: attackElement = DAMAGE_FIRE; - uDamageAmount = character->CalculateRangedDamageTo(0); + uDamageAmount = character->CalculateRangedDamageTo(MONSTER_0); if (pMonster->buffs[ACTOR_BUFF_SHIELD].Active()) uDamageAmount >>= 1; IsAdditionalDamagePossible = true; @@ -3334,7 +3347,7 @@ void Actor::DamageMonsterFromParty(Pid a1, unsigned int uActorID_Monster, } //----- (004BBF61) -------------------------------------------------------- -void Actor::Arena_summon_actor(int monster_id, Vec3i pos) { +void Actor::Arena_summon_actor(MONSTER_TYPE monster_id, Vec3i pos) { Actor *actor = AllocateActor(true); if (!actor) return; @@ -3347,13 +3360,13 @@ void Actor::Arena_summon_actor(int monster_id, Vec3i pos) { actor->currentHP = (short)pMonsterStats->pInfos[monster_id].uHP; actor->monsterInfo = pMonsterStats->pInfos[monster_id]; actor->word_000086_some_monster_id = monster_id; - actor->radius = pMonsterList->pMonsters[monster_id - 1].uMonsterRadius; - actor->height = pMonsterList->pMonsters[monster_id - 1].uMonsterHeight; - actor->moveSpeed = pMonsterList->pMonsters[monster_id - 1].uMovementSpeed; + actor->radius = pMonsterList->pMonsters[monster_id].uMonsterRadius; + actor->height = pMonsterList->pMonsters[monster_id].uMonsterHeight; + actor->moveSpeed = pMonsterList->pMonsters[monster_id].uMovementSpeed; actor->initialPosition = pos; actor->pos = pos; actor->attributes |= ACTOR_AGGRESSOR; - actor->monsterInfo.uTreasureType = 0; + actor->monsterInfo.uTreasureType = RANDOM_ITEM_ANY; actor->monsterInfo.uTreasureLevel = ITEM_TREASURE_LEVEL_INVALID; actor->monsterInfo.uTreasureDiceSides = 0; actor->monsterInfo.uTreasureDiceRolls = 0; @@ -3661,7 +3674,7 @@ bool CheckActors_proximity() { pActors[i].aiState != Disabled && pActors[i].aiState != Summoned && (pActors[i].ActorEnemy() || - pActors[i].GetActorsRelation(0))) + pActors[i].GetActorsRelation(0) != MonsterInfo::Hostility_Friendly)) return true; } } @@ -3833,7 +3846,7 @@ bool Actor::_427102_IsOkToCastSpell(SpellId spell) { //----- (0042704B) -------------------------------------------------------- ABILITY_INDEX Actor::special_ability_use_check(int a2) { - if (this->monsterInfo.uSpecialAbilityType == 2 && this->monsterInfo.uSpecialAbilityDamageDiceBonus < 3 && grng->random(100) < 5) + if (this->monsterInfo.uSpecialAbilityType == MONSTER_SPECIAL_ABILITY_SUMMON && this->monsterInfo.uSpecialAbilityDamageDiceBonus < 3 && grng->random(100) < 5) this->SummonMinion(a2); bool okToCastSpell1 = this->_427102_IsOkToCastSpell(this->monsterInfo.uSpell1ID); @@ -4063,7 +4076,7 @@ void Actor::MakeActorAIList_ODM() { if (distance < 5632) { actor.ResetHostile(); - if (actor.ActorEnemy() || actor.GetActorsRelation(0)) { + if (actor.ActorEnemy() || actor.GetActorsRelation(0) != MonsterInfo::Hostility_Friendly) { actor.attributes |= ACTOR_HOSTILE; if (distance < 5120) pParty->SetYellowAlert(); @@ -4117,7 +4130,7 @@ int Actor::MakeActorAIList_BLV() { // actor is in range if (distance < 10240) { actor.ResetHostile(); - if (actor.ActorEnemy() || actor.GetActorsRelation(0)) { + if (actor.ActorEnemy() || actor.GetActorsRelation(0) != MonsterInfo::Hostility_Friendly) { actor.attributes |= ACTOR_HOSTILE; if (!(pParty->GetRedAlert()) && (double)distance < 307.2) pParty->SetRedAlert(); @@ -4350,7 +4363,7 @@ void Spawn_Light_Elemental(int spell_power, CharacterSkillMastery caster_skill_m cMonsterName = "Elemental Light B"; else cMonsterName = "Elemental Light A"; - unsigned int uMonsterID = pMonsterList->GetMonsterIDByName(cMonsterName); + MONSTER_TYPE uMonsterID = pMonsterList->GetMonsterIDByName(cMonsterName); Actor *actor = AllocateActor(false); if (!actor) @@ -4363,14 +4376,14 @@ void Spawn_Light_Elemental(int spell_power, CharacterSkillMastery caster_skill_m int radius = uCurrentlyLoadedLevelType == LEVEL_OUTDOOR ? 128 : 64; int angle = grng->random(2048); - actor->name = pMonsterStats->pInfos[uMonsterID + 1].pName; - actor->currentHP = pMonsterStats->pInfos[uMonsterID + 1].uHP; - actor->monsterInfo = pMonsterStats->pInfos[uMonsterID + 1]; - actor->word_000086_some_monster_id = uMonsterID + 1; + actor->name = pMonsterStats->pInfos[uMonsterID].pName; + actor->currentHP = pMonsterStats->pInfos[uMonsterID].uHP; + actor->monsterInfo = pMonsterStats->pInfos[uMonsterID]; + actor->word_000086_some_monster_id = uMonsterID; actor->radius = pMonsterList->pMonsters[uMonsterID].uMonsterRadius; actor->height = pMonsterList->pMonsters[uMonsterID].uMonsterHeight; actor->monsterInfo.uTreasureDiceRolls = 0; - actor->monsterInfo.uTreasureType = 0; + actor->monsterInfo.uTreasureType = RANDOM_ITEM_ANY; actor->monsterInfo.uExp = 0; actor->moveSpeed = pMonsterList->pMonsters[uMonsterID].uMovementSpeed; actor->initialPosition.x = pParty->pos.x + TrigLUT.cos(angle) * radius; @@ -4419,7 +4432,7 @@ void SpawnEncounter(MapInfo *pMapInfo, SpawnPoint *spawn, int a3, int a4, int a5 int v24; // edi@36 int v25; // ecx@36 MonsterDesc *v27; // edi@48 - signed int v28; // eax@48 + MONSTER_TYPE v28; // eax@48 int v32; // eax@50 int v37; // eax@51 int v38; // eax@52 @@ -4592,16 +4605,12 @@ void SpawnEncounter(MapInfo *pMapInfo, SpawnPoint *spawn, int a3, int a4, int a5 } } - v50 = pMonsterList->GetMonsterIDByName(Str2); + MONSTER_TYPE v50 = pMonsterList->GetMonsterIDByName(Str2); pTexture = Str2; - if ((int16_t)v50 == -1) { - logger->warning("Can't create random monster: '{}'! See MapStats.txt and Monsters.txt!", pTexture); - Error("Can't create random monster!"); - } - v27 = &pMonsterList->pMonsters[(int16_t)v50]; + v27 = &pMonsterList->pMonsters[v50]; v28 = pMonsterStats->FindMonsterByTextureName(pTexture); - if (!v28) v28 = 1; + if (v28 == MONSTER_0) v28 = MONSTER_1; Src = &pMonsterStats->pInfos[v28]; pMonster->name = Src->pName; pMonster->currentHP = Src->uHP; @@ -4610,7 +4619,7 @@ void SpawnEncounter(MapInfo *pMapInfo, SpawnPoint *spawn, int a3, int a4, int a5 pMonster->monsterInfo = pMonsterStats->pInfos[v28]; - pMonster->word_000086_some_monster_id = v50 + 1; + pMonster->word_000086_some_monster_id = v50; pMonster->radius = v27->uMonsterRadius; pMonster->height = v27->uMonsterHeight; pMonster->moveSpeed = v27->uMovementSpeed; @@ -4745,7 +4754,7 @@ void evaluateAoeDamage() { Actor::DamageMonsterFromParty(attack.pid, actorID, &attackVector); break; case OBJECT_Actor: - if (pSpriteObj && pActors[attackerId].GetActorsRelation(&pActors[actorID])) { + if (pSpriteObj && pActors[attackerId].GetActorsRelation(&pActors[actorID]) != MonsterInfo::Hostility_Friendly) { Actor::ActorDamageFromMonster(attack.pid, actorID, &attackVector, pSpriteObj->spellCasterAbility); } break; diff --git a/src/Engine/Objects/Actor.h b/src/Engine/Objects/Actor.h index 25d6bf04a470..0a514cf8321c 100644 --- a/src/Engine/Objects/Actor.h +++ b/src/Engine/Objects/Actor.h @@ -61,7 +61,7 @@ class Actor { void Remove(); void PrepareSprites(char load_sounds_if_bit1_set); void UpdateAnimation(); - signed int GetActorsRelation(Actor *a2); + MonsterInfo::HostilityRadius GetActorsRelation(Actor *a2); void SetRandomGoldIfTheresNoItem(); bool CanAct(); bool IsNotAlive(); @@ -159,7 +159,7 @@ class Actor { int _43B3E0_CalcDamage(ABILITY_INDEX dmgSource); static void AddOnDamageOverlay(unsigned int uActorID, int overlayType, int damage); - static void Arena_summon_actor(int monster_id, Vec3i pos); + static void Arena_summon_actor(MONSTER_TYPE monster_id, Vec3i pos); static void DamageMonsterFromParty(Pid a1, unsigned int uActorID_Monster, Vec3i *pVelocity); static void MakeActorAIList_ODM(); @@ -183,7 +183,7 @@ class Actor { /** * @offset 0x408A7E */ - static int searchDeadActorsByMonsterID(int *pTotalActors, int monsterID); + static int searchDeadActorsByMonsterID(int *pTotalActors, MONSTER_TYPE monsterID); /** * @offset 0x408AE7 @@ -210,7 +210,7 @@ class Actor { int16_t currentHP = 0; MonsterInfo monsterInfo; int16_t word_000084_range_attack = 0; - int16_t word_000086_some_monster_id = 0; // base monster class monsterlist id + MONSTER_TYPE word_000086_some_monster_id = MONSTER_0; // base monster class monsterlist id uint16_t radius = 32; uint16_t height = 128; uint16_t moveSpeed = 200; @@ -233,7 +233,7 @@ class Actor { IndexedArray buffs; std::array items; unsigned int group = 0; - unsigned int ally = 0; + unsigned int ally = 0; // TODO(captainurist): This is (MONSTER_TYPE - 1) / 3 + 1 std::array scheduledJobs; Pid summonerId; Pid lastCharacterIdToHit; diff --git a/src/Engine/Objects/Character.cpp b/src/Engine/Objects/Character.cpp index ac2cc30c7504..5b674a0d449e 100644 --- a/src/Engine/Objects/Character.cpp +++ b/src/Engine/Objects/Character.cpp @@ -1072,7 +1072,7 @@ int Character::GetMeleeDamageMaximal() const { //----- (0048CDDB) -------------------------------------------------------- int Character::CalculateMeleeDamageTo(bool ignoreSkillBonus, bool ignoreOffhand, - unsigned int uTargetActorID) { + MONSTER_TYPE uTargetActorID) { int mainWpnDmg = 0; int offHndWpnDmg = 0; @@ -1124,7 +1124,7 @@ int Character::CalculateMeleeDamageTo(bool ignoreSkillBonus, bool ignoreOffhand, } int Character::CalculateMeleeDmgToEnemyWithWeapon(ItemGen *weapon, - unsigned int uTargetActorID, + MONSTER_TYPE uTargetActorID, bool addOneDice) { ItemId itemId = weapon->uItemID; int diceCount = pItemTable->pItems[itemId].uDamageDice; @@ -1141,7 +1141,7 @@ int Character::CalculateMeleeDmgToEnemyWithWeapon(ItemGen *weapon, int totalDmg = pItemTable->pItems[itemId].uDamageMod + diceResult; // add modifer - if (uTargetActorID > 0) { // if an actor has been provided + if (uTargetActorID > MONSTER_0) { // if an actor has been provided ITEM_ENCHANTMENT enchType = weapon->special_enchantment; // check against enchantments @@ -1228,7 +1228,7 @@ int Character::GetRangedDamageMax() { } //----- (0048D1FE) -------------------------------------------------------- -int Character::CalculateRangedDamageTo(int uMonsterInfoID) { +int Character::CalculateRangedDamageTo(MONSTER_TYPE uMonsterInfoID) { if (!HasItemEquipped(ITEM_SLOT_BOW)) // no bow return 0; @@ -1245,7 +1245,7 @@ int Character::CalculateRangedDamageTo(int uMonsterInfoID) { damage = pItemTable->pItems[bow->uItemID].uDamageMod + damagefromroll; // total damage - if (uMonsterInfoID) { // check against bow enchantments + if (uMonsterInfoID != MONSTER_0) { // check against bow enchantments if (itemenchant == ITEM_ENCHANTMENT_UNDEAD_SLAYING && MonsterStats::BelongsToSupertype(uMonsterInfoID, MONSTER_SUPERTYPE_UNDEAD)) { // double damage vs undead damage *= 2; @@ -1503,9 +1503,8 @@ int Character::StealFromShop( } //----- (0048D88B) -------------------------------------------------------- -int Character::StealFromActor( - unsigned int uActorID, int _steal_perm, - int reputation) { // returns not used - should luck attribute affect +StealResult Character::StealFromActor(unsigned int uActorID, int _steal_perm, int reputation) { + // TODO(captainurist): returns not used - should luck attribute affect? Actor *actroPtr; actroPtr = &pActors[uActorID]; @@ -1649,12 +1648,7 @@ int Character::receiveDamage(signed int amount, DAMAGE_TYPE dmg_type) { } //----- (0048DCF6) -------------------------------------------------------- -int Character::ReceiveSpecialAttackEffect( - int attType, - Actor *pActor) { // long function - consider breaking into two?? - - SPECIAL_ATTACK_TYPE attTypeCast = (SPECIAL_ATTACK_TYPE)attType; - +int Character::ReceiveSpecialAttackEffect(SPECIAL_ATTACK_TYPE attType, Actor *pActor) { // long function - consider breaking into two?? int statcheck; int statcheckbonus; int luckstat = GetActualLuck(); @@ -1664,7 +1658,7 @@ int Character::ReceiveSpecialAttackEffect( ItemGen *itemtobreak = nullptr; unsigned int itemtostealinvindex = 0; - switch (attTypeCast) { + switch (attType) { case SPECIAL_ATTACK_CURSE: statcheck = GetActualPersonality(); statcheckbonus = GetParameterBonus(statcheck); @@ -1816,7 +1810,7 @@ int Character::ReceiveSpecialAttackEffect( // pass this to new fucntion?? // atttypecast - whichplayer - itemtobreak - itemtostealinvindex - switch (attTypeCast) { + switch (attType) { case SPECIAL_ATTACK_CURSE: SetCondition(CONDITION_CURSED, 1); pAudioPlayer->playUISound(SOUND_star1); @@ -6575,8 +6569,8 @@ void DamageCharacterFromMonster(Pid uObjID, ABILITY_INDEX dmgSource, Vec3i *pPos } // special attack trigger - if (!engine->config->debug.NoDamage.value() && actorPtr->monsterInfo.uSpecialAttackType && grng->random(100) < actorPtr->monsterInfo.uLevel * - actorPtr->monsterInfo.uSpecialAttackLevel) { + if (!engine->config->debug.NoDamage.value() && actorPtr->monsterInfo.uSpecialAttackType != SPECIAL_ATTACK_NONE && + grng->random(100) < actorPtr->monsterInfo.uLevel * actorPtr->monsterInfo.uSpecialAttackLevel) { playerPtr->ReceiveSpecialAttackEffect(actorPtr->monsterInfo.uSpecialAttackType, actorPtr); } @@ -6628,7 +6622,7 @@ void DamageCharacterFromMonster(Pid uObjID, ABILITY_INDEX dmgSource, Vec3i *pPos spritefrom->spell_skill, playerMaxHp); damagetype = pSpellStats->pInfos[spritefrom->uSpellID].damageType; } else { - damage = pParty->pCharacters[uActorID].CalculateRangedDamageTo(0); + damage = pParty->pCharacters[uActorID].CalculateRangedDamageTo(MONSTER_0); damagetype = DAMAGE_FIRE; // TODO(captainurist): doesn't look like a proper default. } playerPtr->receiveDamage(damage, damagetype); @@ -6745,8 +6739,8 @@ void DamageCharacterFromMonster(Pid uObjID, ABILITY_INDEX dmgSource, Vec3i *pPos // special attack trigger if (dmgSource == ABILITY_ATTACK1 && !engine->config->debug.NoDamage.value() && - actorPtr->monsterInfo.uSpecialAttackType && grng->random(100) < actorPtr->monsterInfo.uLevel * - actorPtr->monsterInfo.uSpecialAttackLevel) { + actorPtr->monsterInfo.uSpecialAttackType != SPECIAL_ATTACK_NONE && + grng->random(100) < actorPtr->monsterInfo.uLevel * actorPtr->monsterInfo.uSpecialAttackLevel) { playerPtr->ReceiveSpecialAttackEffect(actorPtr->monsterInfo.uSpecialAttackType, actorPtr); } @@ -6772,7 +6766,7 @@ void DamageCharacterFromMonster(Pid uObjID, ABILITY_INDEX dmgSource, Vec3i *pPos spritefrom->spell_skill, playerMaxHp); damagetype = pSpellStats->pInfos[spritefrom->uSpellID].damageType; } else { - damage = pParty->pCharacters[uActorID].CalculateRangedDamageTo(0); + damage = pParty->pCharacters[uActorID].CalculateRangedDamageTo(MONSTER_0); damagetype = DAMAGE_FIRE; // TODO(captainurist): another weird default. } @@ -7436,7 +7430,7 @@ bool Character::isClass(CharacterClassType class_type, bool check_honorary) cons } //----- (00490EEE) -------------------------------------------------------- -MerchantPhrase Character::SelectPhrasesTransaction(ItemGen *pItem, BuildingType building_type, HOUSE_ID houseId, int ShopMenuType) { +MerchantPhrase Character::SelectPhrasesTransaction(ItemGen *pItem, BuildingType building_type, HOUSE_ID houseId, ShopScreen ShopMenuType) { // TODO(_): probably move this somewhere else, not really Character:: stuff ItemId idemId; // edx@1 ITEM_EQUIP_TYPE equipType; // esi@1 @@ -7484,19 +7478,19 @@ MerchantPhrase Character::SelectPhrasesTransaction(ItemGen *pItem, BuildingType multiplier = buildingTable[houseId].fPriceMultiplier; switch (ShopMenuType) { - case 2: + case SHOP_SCREEN_BUY: price = PriceCalculator::itemBuyingPriceForPlayer(this, itemValue, multiplier); break; - case 3: + case SHOP_SCREEN_SELL: // if (pItem->IsBroken()) // price = 1; // else price = PriceCalculator::itemSellingPriceForPlayer(this, *pItem, multiplier); break; - case 4: + case SHOP_SCREEN_IDENTIFY: price = PriceCalculator::itemIdentificationPriceForPlayer(this, multiplier); break; - case 5: + case SHOP_SCREEN_REPAIR: price = PriceCalculator::itemRepairPriceForPlayer(this, itemValue, multiplier); break; default: diff --git a/src/Engine/Objects/Character.h b/src/Engine/Objects/Character.h index ab4c6c97453f..aa6da2ed751b 100644 --- a/src/Engine/Objects/Character.h +++ b/src/Engine/Objects/Character.h @@ -11,6 +11,7 @@ #include "Engine/Objects/Items.h" #include "Engine/Objects/ItemEnums.h" #include "Engine/Objects/CharacterEnums.h" +#include "Engine/Objects/Monsters.h" // TODO(captainurist): MonsterEnums.h #include "Engine/Spells/SpellEnums.h" #include "Engine/Spells/SpellBuff.h" #include "Engine/Tables/BuildingTable.h" @@ -18,6 +19,8 @@ #include "Engine/ErrorHandling.h" #include "Engine/Pid.h" +#include "GUI/GUIEnums.h" + #include "Library/Color/Color.h" #include "Utility/Geometry/Vec.h" @@ -27,6 +30,13 @@ class Actor; class GraphicsImage; +enum class StealResult { + STEAL_BUSTED = 0, // Failed to steal & was caught. + STEAL_NOTHING = 1, // Either failed to steal, or there was nothing to steal. + STEAL_SUCCESS = 2, // Stolen successfully. +}; +using enum StealResult; + struct LloydBeacon { ~LloydBeacon() { // if (image != nullptr) { @@ -79,13 +89,6 @@ union CharacterEquipment { CharacterEquipment() : pIndices() {} }; - -// TODO(captainurist): ENUM! -#define STEAL_BUSTED 0 -#define STEAL_NOTHING 1 -#define STEAL_SUCCESS 2 - - class CharacterConditions { public: [[nodiscard]] bool Has(Condition condition) const { @@ -175,11 +178,11 @@ class Character { int GetMeleeDamageMinimal() const; int GetMeleeDamageMaximal() const; int CalculateMeleeDamageTo(bool ignoreSkillBonus, bool ignoreOffhand, - unsigned int uTargetActorID); + MONSTER_TYPE uTargetActorID); int GetRangedAttack(); int GetRangedDamageMin(); int GetRangedDamageMax(); - int CalculateRangedDamageTo(int uMonsterInfoID); + int CalculateRangedDamageTo(MONSTER_TYPE uMonsterInfoID); std::string GetMeleeDamageString(); std::string GetRangedDamageString(); bool CanTrainToNextLevel(); @@ -193,14 +196,14 @@ class Character { bool WearsItem(ItemId item_id, ItemSlot equip_type) const; int StealFromShop(ItemGen *itemToSteal, int extraStealDifficulty, int reputation, int extraStealFine, int *fineIfFailed); - int StealFromActor(unsigned int uActorID, int _steal_perm, int reputation); + StealResult StealFromActor(unsigned int uActorID, int _steal_perm, int reputation); void Heal(int amount); /** * @offset 0x48DC1E */ int receiveDamage(signed int amount, DAMAGE_TYPE dmg_type); - int ReceiveSpecialAttackEffect(int attType, Actor *pActor); + int ReceiveSpecialAttackEffect(SPECIAL_ATTACK_TYPE attType, Actor *pActor); // TODO(captainurist): move closer to Spells data. DAMAGE_TYPE GetSpellDamageType(SpellId uSpellID) const; @@ -239,7 +242,7 @@ class Character { void resetTempBonuses(); Color GetStatColor(CharacterAttributeType uStat) const; bool DiscardConditionIfLastsLongerThan(Condition uCondition, GameTime time); - MerchantPhrase SelectPhrasesTransaction(ItemGen *pItem, BuildingType building_type, HOUSE_ID houseId, int ShopMenuType); + MerchantPhrase SelectPhrasesTransaction(ItemGen *pItem, BuildingType building_type, HOUSE_ID houseId, ShopScreen ShopMenuType); int GetBodybuilding() const; int GetMeditation() const; bool CanIdentify(ItemGen *pItem) const; @@ -318,7 +321,7 @@ class Character { unsigned int GetMultiplierForSkillLevel(CharacterSkillType uSkillType, int mult1, int mult2, int mult3, int mult4) const; int CalculateMeleeDmgToEnemyWithWeapon(ItemGen *weapon, - unsigned int uTargetActorID, + MONSTER_TYPE uTargetActorID, bool addOneDice); bool wearsItemAnywhere(ItemId item_id) const; float GetArmorRecoveryMultiplierFromSkillLevel(CharacterSkillType armour_skill_type, float param2, float param3, float param4, float param5) const; diff --git a/src/Engine/Objects/Chest.cpp b/src/Engine/Objects/Chest.cpp index 4aba51d89e30..e5b579bc52dc 100644 --- a/src/Engine/Objects/Chest.cpp +++ b/src/Engine/Objects/Chest.cpp @@ -554,7 +554,7 @@ void GenerateItemsInChest() { additionaItemCount++; // + 1 because it's the item at pChests[i].igChestItems[j] and the additional ones ItemTreasureLevel resultTreasureLevel = grng->randomSample( RemapTreasureLevel(randomItemTreasureLevel(currItem->uItemID), currMapInfo->Treasure_prob)); - if (resultTreasureLevel != ITEM_TREASURE_LEVEL_GUARANTEED_ARTIFACT) { + if (resultTreasureLevel != ITEM_TREASURE_LEVEL_7) { for (int k = 0; k < additionaItemCount; k++) { int whatToGenerateProb = grng->random(100); if (whatToGenerateProb < 20) { @@ -595,7 +595,7 @@ void GenerateItemsInChest() { currItem->SetIdentified(); currItem->special_enchantment = (ITEM_ENCHANTMENT)goldAmount; } else { - pItemTable->generateItem(resultTreasureLevel, 0, currItem); + pItemTable->generateItem(resultTreasureLevel, RANDOM_ITEM_ANY, currItem); } for (int m = 0; m < 140; m++) { diff --git a/src/Engine/Objects/ItemEnums.h b/src/Engine/Objects/ItemEnums.h index c4f7bf25e5e1..131ca1158574 100644 --- a/src/Engine/Objects/ItemEnums.h +++ b/src/Engine/Objects/ItemEnums.h @@ -145,10 +145,7 @@ enum class ItemTreasureLevel : int8_t { ITEM_TREASURE_LEVEL_4 = 4, ITEM_TREASURE_LEVEL_5 = 5, ITEM_TREASURE_LEVEL_6 = 6, // 5% chance for an artifact. - ITEM_TREASURE_LEVEL_GUARANTEED_ARTIFACT = 7, - - ITEM_TREASURE_LEVEL_FIRST_VALID = ITEM_TREASURE_LEVEL_1, - ITEM_TREASURE_LEVEL_LAST_VALID = ITEM_TREASURE_LEVEL_GUARANTEED_ARTIFACT, + ITEM_TREASURE_LEVEL_7 = 7, // Guaranteed artifact. ITEM_TREASURE_LEVEL_FIRST_RANDOM = ITEM_TREASURE_LEVEL_1, ITEM_TREASURE_LEVEL_LAST_RANDOM = ITEM_TREASURE_LEVEL_6, @@ -998,12 +995,19 @@ enum class ItemId : int32_t { ITEM_FIRST_RECIPE = ITEM_RECIPE_REJUVENATION, ITEM_LAST_RECIPE = ITEM_RECIPE_BODY_RESISTANCE, + ITEM_FIRST_QUEST = ITEM_QUEST_HEART_OF_THE_WOOD, + ITEM_LAST_QUEST = ITEM_699, + ITEM_FIRST_REAGENT = ITEM_REAGENT_WIDOWSWEEP_BERRIES, ITEM_LAST_REAGENT = ITEM_REAGENT_PHILOSOPHERS_STONE, ITEM_FIRST_REAL_POTION = ITEM_POTION_CURE_WOUNDS, ITEM_LAST_REAL_POTION = ITEM_POTION_REJUVENATION, + // TODO(captainurist): ITEM_POTION_BOTTLE equip type is EQUIP_POTION, but we don't have an empty bottle in the range below. Not good. + ITEM_FIRST_POTION = ITEM_POTION_CATALYST, + ITEM_LAST_POTION = ITEM_POTION_REJUVENATION, + ITEM_FIRST_WAND = ITEM_WAND_OF_FIRE, ITEM_LAST_WAND = ITEM_MYSTIC_WAND_OF_INCINERATION, @@ -1013,10 +1017,6 @@ enum class ItemId : int32_t { ITEM_FIRST_SPELLBOOK = ITEM_SPELLBOOK_TORCH_LIGHT, ITEM_LAST_SPELLBOOK = ITEM_SPELLBOOK_SOULDRINKER, - // TODO(captainurist): ITEM_POTION_BOTTLE equip type is EQUIP_POTION, but we don't have an empty bottle in the range below. Not good. - ITEM_FIRST_POTION = ITEM_POTION_CATALYST, - ITEM_LAST_POTION = ITEM_POTION_REJUVENATION, - ITEM_FIRST_SPAWNABLE = ITEM_CRUDE_LONGSWORD, ITEM_LAST_SPAWNABLE = ITEM_499, @@ -1087,6 +1087,10 @@ inline bool isSpellbook(ItemId item) { return item >= ITEM_FIRST_SPELLBOOK && item <= ITEM_LAST_SPELLBOOK; } +inline bool isQuestItem(ItemId item) { + return item >= ITEM_FIRST_QUEST && item <= ITEM_LAST_QUEST; +} + inline ItemTreasureLevel randomItemTreasureLevel(ItemId type) { assert(isRandomItem(type)); return ItemTreasureLevel(-std::to_underlying(type)); @@ -1284,3 +1288,53 @@ inline ItemSlot ringSlot(int index) { inline Segment allItemSlots() { return Segment(ITEM_SLOT_FIRST_VALID, ITEM_SLOT_LAST_VALID); } + +/** + * Item types used in random item creation code. + * + * @see https://github.com/GrayFace/MMExtension/blob/4d6600f164315f38157591d7f0307a86594c22ef/Scripts/Core/ConstAndBits.lua#L592 + */ +enum class RandomItemType { + RANDOM_ITEM_ANY = 0, + + // TODO(captainurist): Values in 1-19 are ITEM_EQUIP_TYPE + 1, but those are not used in MM7? + // See code in ItemTable::generateItem. + + RANDOM_ITEM_WEAPON = 20, // A single-handed weapon. + RANDOM_ITEM_ARMOR = 21, + RANDOM_ITEM_MICS = 22, // For items requiring CHARACTER_SKILL_MISC - magic shop items. + RANDOM_ITEM_SWORD = 23, + RANDOM_ITEM_DAGGER = 24, + RANDOM_ITEM_AXE = 25, + RANDOM_ITEM_SPEAR = 26, + RANDOM_ITEM_BOW = 27, + RANDOM_ITEM_MACE = 28, + RANDOM_ITEM_CLUB = 29, // For items requiring CHARACTER_SKILL_CLUB, which is a hidden skill. + RANDOM_ITEM_STAFF = 30, + RANDOM_ITEM_LEATHER_ARMOR = 31, + RANDOM_ITEM_CHAIN_ARMOR = 32, + RANDOM_ITEM_PLATE_ARMOR = 33, + RANDOM_ITEM_SHIELD = 34, + RANDOM_ITEM_HELMET = 35, + RANDOM_ITEM_BELT = 36, + RANDOM_ITEM_CLOAK = 37, + RANDOM_ITEM_GAUNTLETS = 38, + RANDOM_ITEM_BOOTS = 39, + RANDOM_ITEM_RING = 40, + RANDOM_ITEM_AMULET = 41, + RANDOM_ITEM_WAND = 42, + RANDOM_ITEM_SPELL_SCROLL = 43, + RANDOM_ITEM_POTION = 44, + RANDOM_ITEM_REAGENT = 45, + RANDOM_ITEM_GEM = 46, + // MMExtension also has Gems2 = 47 + // and Gold_ = 50 + + RANDOM_ITEM_FIRST_SPAWNABLE = RANDOM_ITEM_WEAPON, + RANDOM_ITEM_LAST_SPAWNABLE = RANDOM_ITEM_GEM +}; +using enum RandomItemType; + +inline Segment allSpawnableRandomItemTypes() { + return {RANDOM_ITEM_FIRST_SPAWNABLE, RANDOM_ITEM_LAST_SPAWNABLE}; +} diff --git a/src/Engine/Objects/Items.cpp b/src/Engine/Objects/Items.cpp index 21b5a6854b04..33f2393829c8 100644 --- a/src/Engine/Objects/Items.cpp +++ b/src/Engine/Objects/Items.cpp @@ -744,44 +744,38 @@ std::string GetItemTextureFilename(ItemId item_id, int index, int shoulder) { } //----- (004BDAAF) -------------------------------------------------------- -bool ItemGen::MerchandiseTest(HOUSE_ID houseId) { - bool test; - - // TODO(captainurist): move these checks into functions in ItemEnums.h? - if ((buildingTable[houseId].uType != BUILDING_ALCHEMY_SHOP || !isRecipe(this->uItemID)) && - (this->uItemID >= ITEM_QUEST_HEART_OF_THE_WOOD || this->uItemID >= ITEM_ARTIFACT_HERMES_SANDALS && this->uItemID <= ITEM_599) || - this->IsStolen()) +bool ItemGen::canSellRepairIdentifyAt(HOUSE_ID houseId) { + if (this->IsStolen()) return false; + if (isQuestItem(uItemID)) + return false; // Can't sell quest items. + + if (isArtifact(uItemID) && !isSpawnableArtifact(uItemID)) + return false; // Can't sell quest artifacts, e.g. Hermes Sandals. + + if (::isMessageScroll(uItemID) && !isRecipe(uItemID)) + return false; // Can't sell message scrolls. Recipes are sellable at alchemy shops. + switch (buildingTable[houseId].uType) { - case BUILDING_WEAPON_SHOP: { - test = this->isWeapon(); - break; - } - case BUILDING_ARMOR_SHOP: { - test = this->isArmor(); - break; - } - case BUILDING_MAGIC_SHOP: { - test = this->GetPlayerSkillType() == CHARACTER_SKILL_MISC || this->isBook(); - break; - } - case BUILDING_ALCHEMY_SHOP: { - test = this->isReagent() || + case BUILDING_WEAPON_SHOP: + return this->isWeapon(); + case BUILDING_ARMOR_SHOP: + return this->isArmor(); + case BUILDING_MAGIC_SHOP: + return this->GetPlayerSkillType() == CHARACTER_SKILL_MISC || this->isBook(); + case BUILDING_ALCHEMY_SHOP: + return this->isReagent() || this->isPotion() || (this->isMessageScroll() && isRecipe(this->uItemID)); - break; - } - default: { - test = false; - break; - } + default: + return false; } - return test; } Segment RemapTreasureLevel(ItemTreasureLevel itemTreasureLevel, MAP_TREASURE_LEVEL mapTreasureLevel) { - // mapping[item_level][map_level] -> [actual_level_min, actual_level_max]; + // Mapping [item_level][map_level] -> [actual_level_min, actual_level_max]; + // Rows are item treasure levels, columns are map treasure levels. Not using IndexedArray to keep things terse. // Original offset was 0x004E8168. static constexpr std::array, 7>, 7> mapping = {{ {{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}}, @@ -792,10 +786,14 @@ Segment RemapTreasureLevel(ItemTreasureLevel itemTreasureLeve {{{2, 2}, {2, 2}, {4, 4}, {4, 5}, {5, 5}, {5, 6}, {6, 6}}}, {{{2, 2}, {2, 2}, {7, 7}, {7, 7}, {7, 7}, {7, 7}, {7, 7}}} }}; + static_assert(std::to_underlying(ITEM_TREASURE_LEVEL_1) == 1); + static_assert(std::to_underlying(ITEM_TREASURE_LEVEL_7) == 7); // Otherwise static_casts at the end of this function won't work. + + assert(itemTreasureLevel >= ITEM_TREASURE_LEVEL_1 && ITEM_TREASURE_LEVEL_1 <= ITEM_TREASURE_LEVEL_7); + assert(mapTreasureLevel >= MAP_TREASURE_LEVEL_1 && mapTreasureLevel <= MAP_TREASURE_LEVEL_7); - // TODO(captainurist) : type-safe enum diff! - int itemIdx = std::to_underlying(itemTreasureLevel) - std::to_underlying(ITEM_TREASURE_LEVEL_FIRST_VALID); - int mapIdx = std::to_underlying(mapTreasureLevel) - std::to_underlying(MAP_TREASURE_LEVEL_FIRST); + int itemIdx = std::to_underlying(itemTreasureLevel) - std::to_underlying(ITEM_TREASURE_LEVEL_1); + int mapIdx = std::to_underlying(mapTreasureLevel) - std::to_underlying(MAP_TREASURE_LEVEL_1); Segment result = mapping[itemIdx][mapIdx]; - return {ItemTreasureLevel(result.front()), ItemTreasureLevel(result.back())}; + return {static_cast(result.front()), static_cast(result.back())}; } diff --git a/src/Engine/Objects/Items.h b/src/Engine/Objects/Items.h index 5bed42a9a0c5..82f22fa6774c 100644 --- a/src/Engine/Objects/Items.h +++ b/src/Engine/Objects/Items.h @@ -64,7 +64,7 @@ struct ItemGen { // 0x24 uint8_t GetDamageDice() const; uint8_t GetDamageRoll() const; uint8_t GetDamageMod() const; - bool MerchandiseTest(HOUSE_ID houseId); + bool canSellRepairIdentifyAt(HOUSE_ID houseId); bool isGold() const { return GetItemEquipType() == EQUIP_GOLD; diff --git a/src/Engine/Objects/Monsters.cpp b/src/Engine/Objects/Monsters.cpp index e92301697909..67000dbf219b 100644 --- a/src/Engine/Objects/Monsters.cpp +++ b/src/Engine/Objects/Monsters.cpp @@ -335,15 +335,16 @@ bool MonsterList::FromFileTxt(const char *Args) { v4 = Argsa; } - this->pMonsters.clear(); v6 = File; fseek(v6, 0, 0); + MONSTER_TYPE monsterId = MONSTER_0; for (i = fgets(Buf, sizeof(Buf), File); i; i = fgets(Buf, sizeof(Buf), File)) { *strchr(Buf, 10) = 0; memcpy(&v25, frame_table_txt_parser(Buf, &v24), sizeof(v25)); v8 = 0; if (v25.uPropCount && *v25.pProperties[0] != 47) { - MonsterDesc &monster = this->pMonsters.emplace_back(); + monsterId = static_cast(std::to_underlying(monsterId) + 1); + MonsterDesc &monster = this->pMonsters[monsterId]; monster.pMonsterName = v25.pProperties[0]; @@ -392,12 +393,12 @@ bool MonsterList::FromFileTxt(const char *Args) { } //----- (004563FF) -------------------------------------------------------- -signed int MonsterStats::FindMonsterByTextureName(const std::string &monster_textr_name) { - for (int i = 1; i < uNumMonsters; ++i) { +MONSTER_TYPE MonsterStats::FindMonsterByTextureName(const std::string &monster_textr_name) { + for (MONSTER_TYPE i : pInfos.indices()) { if (!pInfos[i].pName.empty() && iequals(pInfos[i].pPictureName, monster_textr_name)) return i; } - return -1; + return MONSTER_0; } //----- (00454F4E) -------------------------------------------------------- @@ -450,7 +451,7 @@ void MonsterStats::Initialize(const Blob &monsters) { char *tmp_pos; int decode_step; // int item_counter; - int curr_rec_num; + MONSTER_TYPE curr_rec_num; char parse_str[64]; // char Src[120]; FrameTableTxtLine parsed_field; @@ -462,7 +463,7 @@ void MonsterStats::Initialize(const Blob &monsters) { strtok(NULL, "\r"); strtok(NULL, "\r"); uNumMonsters = 265; - curr_rec_num = 0; + curr_rec_num = MONSTER_0; for (i = 0; i < uNumMonsters - 1; ++i) { test_string = strtok(NULL, "\r") + 1; break_loop = false; @@ -480,7 +481,7 @@ void MonsterStats::Initialize(const Blob &monsters) { if (temp_str_len) { switch (decode_step) { case 0: - curr_rec_num = atoi(test_string); + curr_rec_num = static_cast(atoi(test_string)); pInfos[curr_rec_num].uID = curr_rec_num; break; case 1: @@ -546,7 +547,7 @@ void MonsterStats::Initialize(const Blob &monsters) { pInfos[curr_rec_num].uTreasureDropChance = 0; pInfos[curr_rec_num].uTreasureDiceRolls = 0; pInfos[curr_rec_num].uTreasureDiceSides = 0; - pInfos[curr_rec_num].uTreasureType = 0; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_ANY; pInfos[curr_rec_num].uTreasureLevel = ITEM_TREASURE_LEVEL_INVALID; if (test_string[0] == '"') test_string[0] = ' '; str_len = strlen(test_string); @@ -608,55 +609,55 @@ void MonsterStats::Initialize(const Blob &monsters) { item_name = &test_string[str_pos + 2]; if (*item_name) { if (iequals(item_name, "WEAPON")) - pInfos[curr_rec_num].uTreasureType = 20; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_WEAPON; else if (iequals(item_name, "ARMOR")) - pInfos[curr_rec_num].uTreasureType = 21; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_ARMOR; else if (iequals(item_name, "MISC")) - pInfos[curr_rec_num].uTreasureType = 22; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_MICS; else if (iequals(item_name, "SWORD")) - pInfos[curr_rec_num].uTreasureType = 23; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_SWORD; else if (iequals(item_name, "DAGGER")) - pInfos[curr_rec_num].uTreasureType = 24; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_DAGGER; else if (iequals(item_name, "AXE")) - pInfos[curr_rec_num].uTreasureType = 25; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_AXE; else if (iequals(item_name, "SPEAR")) - pInfos[curr_rec_num].uTreasureType = 26; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_SPEAR; else if (iequals(item_name, "BOW")) - pInfos[curr_rec_num].uTreasureType = 27; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_BOW; else if (iequals(item_name, "MACE")) - pInfos[curr_rec_num].uTreasureType = 28; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_MACE; else if (iequals(item_name, "CLUB")) - pInfos[curr_rec_num].uTreasureType = 29; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_CLUB; else if (iequals(item_name, "STAFF")) - pInfos[curr_rec_num].uTreasureType = 30; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_STAFF; else if (iequals(item_name, "LEATHER")) - pInfos[curr_rec_num].uTreasureType = 31; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_LEATHER_ARMOR; else if (iequals(item_name, "CHAIN")) - pInfos[curr_rec_num].uTreasureType = 32; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_CHAIN_ARMOR; else if (iequals(item_name, "PLATE")) - pInfos[curr_rec_num].uTreasureType = 33; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_PLATE_ARMOR; else if (iequals(item_name, "SHIELD")) - pInfos[curr_rec_num].uTreasureType = 34; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_SHIELD; else if (iequals(item_name, "HELM")) - pInfos[curr_rec_num].uTreasureType = 35; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_HELMET; else if (iequals(item_name, "BELT")) - pInfos[curr_rec_num].uTreasureType = 36; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_BELT; else if (iequals(item_name, "CAPE")) - pInfos[curr_rec_num].uTreasureType = 37; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_CLOAK; else if (iequals(item_name, "GAUNTLETS")) - pInfos[curr_rec_num].uTreasureType = 38; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_GAUNTLETS; else if (iequals(item_name, "BOOTS")) - pInfos[curr_rec_num].uTreasureType = 39; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_BOOTS; else if (iequals(item_name, "RING")) - pInfos[curr_rec_num].uTreasureType = 40; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_RING; else if (iequals(item_name, "AMULET")) - pInfos[curr_rec_num].uTreasureType = 41; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_AMULET; else if (iequals(item_name, "WAND")) - pInfos[curr_rec_num].uTreasureType = 42; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_WAND; else if (iequals(item_name, "SCROLL")) - pInfos[curr_rec_num].uTreasureType = 43; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_SPELL_SCROLL; else if (iequals(item_name, "GEM")) - pInfos[curr_rec_num].uTreasureType = 46; + pInfos[curr_rec_num].uTreasureType = RANDOM_ITEM_GEM; } } } break; @@ -962,7 +963,7 @@ void MonsterStats::Initialize(const Blob &monsters) { case 38: { // int param_num; // char type_flag; - pInfos[curr_rec_num].uSpecialAbilityType = 0; + pInfos[curr_rec_num].uSpecialAbilityType = MONSTER_SPECIAL_ABILITY_NONE; pInfos[curr_rec_num].uSpecialAbilityDamageDiceBonus = 0; strcpy(parse_str, test_string); parse_str[0] = ' '; @@ -972,15 +973,13 @@ void MonsterStats::Initialize(const Blob &monsters) { // v74 = v94.field_0; if (parsed_field.uPropCount < 10) { if (iequals(parsed_field.pProperties[0], "shot")) { - pInfos[curr_rec_num].uSpecialAbilityType = - 1; + pInfos[curr_rec_num].uSpecialAbilityType = MONSTER_SPECIAL_ABILITY_SHOT; pInfos[curr_rec_num] .uSpecialAbilityDamageDiceBonus = atoi( (char *)(parsed_field.pProperties[1] + 1)); } else if (iequals(parsed_field.pProperties[0], "summon")) { - pInfos[curr_rec_num].uSpecialAbilityType = - 2; + pInfos[curr_rec_num].uSpecialAbilityType = MONSTER_SPECIAL_ABILITY_SUMMON; if (parsed_field.uPropCount > 1) { str = parsed_field.pProperties[2]; if (parsed_field.uPropCount > 2) { @@ -1032,16 +1031,8 @@ void MonsterStats::Initialize(const Blob &monsters) { 0; } if (!pMonsterList->pMonsters.empty()) { - pInfos[curr_rec_num] - .field_3C_some_special_attack = - pMonsterList - ->GetMonsterIDByName(str) + - 1; - if (pInfos[curr_rec_num] - .field_3C_some_special_attack == - -1) { - logger->warning("Can't create random monster: '{}'. See MapStats!", str); - } + pInfos[curr_rec_num].field_3C_some_special_attack = + std::to_underlying(pMonsterList->GetMonsterIDByName(str)); } pInfos[curr_rec_num] .uSpecialAbilityDamageDiceSides = 0; @@ -1052,12 +1043,10 @@ void MonsterStats::Initialize(const Blob &monsters) { if (pInfos[curr_rec_num] .field_3C_some_special_attack == -1) - pInfos[curr_rec_num] - .uSpecialAbilityType = 0; + pInfos[curr_rec_num].uSpecialAbilityType = MONSTER_SPECIAL_ABILITY_NONE; } } else if (iequals(parsed_field.pProperties[0], "explode")) { - pInfos[curr_rec_num].uSpecialAbilityType = - 3; + pInfos[curr_rec_num].uSpecialAbilityType = MONSTER_SPECIAL_ABILITY_EXPLODE; ParseDamage( (char *)parsed_field.pProperties[1], &pInfos[curr_rec_num] @@ -1085,74 +1074,74 @@ void MonsterStats::Initialize(const Blob &monsters) { } //----- (0044FA08) -------------------------------------------------------- -int16_t MonsterList::GetMonsterIDByName(const std::string &pMonsterName) { - for (int16_t i = 0; i < pMonsters.size(); ++i) { +MONSTER_TYPE MonsterList::GetMonsterIDByName(const std::string &pMonsterName) { + for (MONSTER_TYPE i : pMonsters.indices()) { if (iequals(pMonsters[i].pMonsterName, pMonsterName)) return i; } Error("Monster not found: %s", pMonsterName.c_str()); } //----- (00438BDF) -------------------------------------------------------- -bool MonsterStats::BelongsToSupertype(unsigned int uMonsterInfoID, +bool MonsterStats::BelongsToSupertype(MONSTER_TYPE uMonsterInfoID, enum MONSTER_SUPERTYPE eSupertype) { switch (eSupertype) { case MONSTER_SUPERTYPE_UNDEAD: - if ((signed int)uMonsterInfoID >= MONSTER_GHOST_1 && - (signed int)uMonsterInfoID <= MONSTER_GHOST_3 // 70<=id<=72 - || (signed int)uMonsterInfoID >= MONSTER_LICH_1 && - (signed int)uMonsterInfoID <= MONSTER_LICH_3 // 91-93 + if (uMonsterInfoID >= MONSTER_GHOST_1 && + uMonsterInfoID <= MONSTER_GHOST_3 // 70<=id<=72 + || uMonsterInfoID >= MONSTER_LICH_1 && + uMonsterInfoID <= MONSTER_LICH_3 // 91-93 || - (signed int)uMonsterInfoID >= MONSTER_SKELETON_1 && - (signed int)uMonsterInfoID <= MONSTER_SKELETON_3 // 199-201 + uMonsterInfoID >= MONSTER_SKELETON_1 && + uMonsterInfoID <= MONSTER_SKELETON_3 // 199-201 || - (signed int)uMonsterInfoID >= MONSTER_VAMPIRE_1 && - (signed int)uMonsterInfoID <= MONSTER_VAMPIRE_3 // 217-219 - || (signed int)uMonsterInfoID >= MONSTER_WIGHT_1 && - (signed int)uMonsterInfoID <= MONSTER_WIGHT_3 // 223-225 + uMonsterInfoID >= MONSTER_VAMPIRE_1 && + uMonsterInfoID <= MONSTER_VAMPIRE_3 // 217-219 + || uMonsterInfoID >= MONSTER_WIGHT_1 && + uMonsterInfoID <= MONSTER_WIGHT_3 // 223-225 || - (signed int)uMonsterInfoID >= MONSTER_ZOMBIE_1 && - (signed int)uMonsterInfoID <= MONSTER_ZOMBIE_3 // 229-231 + uMonsterInfoID >= MONSTER_ZOMBIE_1 && + uMonsterInfoID <= MONSTER_ZOMBIE_3 // 229-231 || - (signed int)uMonsterInfoID >= MONSTER_GHOUL_1 && - (signed int)uMonsterInfoID <= MONSTER_GHOUL_3) // 256-258 + uMonsterInfoID >= MONSTER_GHOUL_1 && + uMonsterInfoID <= MONSTER_GHOUL_3) // 256-258 return true; return false; case MONSTER_SUPERTYPE_KREEGAN: - if ((signed int)uMonsterInfoID >= MONSTER_DEVIL_1 && - (signed int)uMonsterInfoID <= MONSTER_DEVIL_3) // 22-24 + if (uMonsterInfoID >= MONSTER_DEVIL_1 && + uMonsterInfoID <= MONSTER_DEVIL_3) // 22-24 return true; return false; case MONSTER_SUPERTYPE_ELF: - if ((signed int)uMonsterInfoID >= MONSTER_PEASANT_ELF_FEMALE_1_1 && - (signed int)uMonsterInfoID <= + if (uMonsterInfoID >= MONSTER_PEASANT_ELF_FEMALE_1_1 && + uMonsterInfoID <= MONSTER_PEASANT_ELF_MALE_3_3 // 133 - 150 || - (signed int)uMonsterInfoID >= MONSTER_ELF_ARCHER_1 && - (signed int)uMonsterInfoID <= MONSTER_ELF_ARCHER_3 // 49-51 - || (signed int)uMonsterInfoID >= MONSTER_ELF_SPEARMAN_1 && - (signed int)uMonsterInfoID <= + uMonsterInfoID >= MONSTER_ELF_ARCHER_1 && + uMonsterInfoID <= MONSTER_ELF_ARCHER_3 // 49-51 + || uMonsterInfoID >= MONSTER_ELF_SPEARMAN_1 && + uMonsterInfoID <= MONSTER_ELF_SPEARMAN_3) // 52-54 return true; return false; case MONSTER_SUPERTYPE_DRAGON: - if ((signed int)uMonsterInfoID >= MONSTER_DRAGON_1 && - (signed int)uMonsterInfoID <= MONSTER_DRAGON_3) // 25-27 + if (uMonsterInfoID >= MONSTER_DRAGON_1 && + uMonsterInfoID <= MONSTER_DRAGON_3) // 25-27 return true; return false; case MONSTER_SUPERTYPE_WATER_ELEMENTAL: - if ((signed int)uMonsterInfoID >= MONSTER_ELEMENTAL_WATER_1 && - (signed int)uMonsterInfoID <= + if (uMonsterInfoID >= MONSTER_ELEMENTAL_WATER_1 && + uMonsterInfoID <= MONSTER_ELEMENTAL_WATER_3) // 46-48 return true; return false; case MONSTER_SUPERTYPE_TREANT: - if ((signed int)uMonsterInfoID >= MONSTER_TREANT_1 && - (signed int)uMonsterInfoID <= MONSTER_TREANT_3) // 253-255 + if (uMonsterInfoID >= MONSTER_TREANT_1 && + uMonsterInfoID <= MONSTER_TREANT_3) // 253-255 return true; return false; case MONSTER_SUPERTYPE_TITAN: - if ((signed int)uMonsterInfoID >= MONSTER_TITAN_1 && - (signed int)uMonsterInfoID <= MONSTER_TITAN_3) // 211-213 + if (uMonsterInfoID >= MONSTER_TITAN_1 && + uMonsterInfoID <= MONSTER_TITAN_3) // 211-213 return true; return false; default: diff --git a/src/Engine/Objects/Monsters.h b/src/Engine/Objects/Monsters.h index 7d5968818003..eb868057eaef 100644 --- a/src/Engine/Objects/Monsters.h +++ b/src/Engine/Objects/Monsters.h @@ -12,66 +12,87 @@ #include "Engine/Spells/SpellEnums.h" #include "Engine/Objects/CombinedSkillValue.h" +#include "Utility/Segment.h" + #include "ActorEnums.h" #include "ItemEnums.h" class Blob; -/* 334 */ -enum MONSTER_TYPE { - MONSTER_DEVIL_1 = 0x16, - MONSTER_DEVIL_2 = 0x17, - MONSTER_DEVIL_3 = 0x18, - MONSTER_DRAGON_1 = 0x19, - MONSTER_DRAGON_2 = 0x1A, - MONSTER_DRAGON_3 = 0x1B, - MONSTER_ELEMENTAL_WATER_1 = 0x2E, - MONSTER_ELEMENTAL_WATER_3 = 0x30, - MONSTER_ELF_ARCHER_1 = 0x31, - MONSTER_ELF_ARCHER_3 = 0x33, - MONSTER_ELF_SPEARMAN_1 = 0x34, - MONSTER_ELF_SPEARMAN_3 = 0x36, - MONSTER_GHOST_1 = 0x46, - MONSTER_GHOST_3 = 0x48, - MONSTER_HARPY_1 = 0x55, - MONSTER_HARPY_2 = 0x56, - MONSTER_HARPY_3 = 0x57, - MONSTER_LICH_1 = 0x5B, - MONSTER_LICH_3 = 0x5D, - MONSTER_OOZE_1 = 0x70, - MONSTER_OOZE_2 = 0x71, - MONSTER_OOZE_3 = 0x72, - MONSTER_PEASANT_ELF_FEMALE_1_1 = 0x85, - MONSTER_PEASANT_ELF_MALE_3_3 = 0x96, - MONSTER_SKELETON_1 = 0xC7, - MONSTER_SKELETON_3 = 0xC9, - MONSTER_TITAN_1 = 0xD3, - MONSTER_TITAN_3 = 0xD5, - MONSTER_VAMPIRE_1 = 0xD9, - MONSTER_VAMPIRE_3 = 0xDB, - MONSTER_WIGHT_1 = 0xDF, - MONSTER_WIGHT_3 = 0xE1, - MONSTER_ZOMBIE_1 = 0xE5, - MONSTER_ZOMBIE_3 = 0xE7, - MONSTER_PEASANT_GOBLIN_MALE_3_3 = 0xF9, - MONSTER_TROLL_1 = 0xFA, - MONSTER_TROLL_2 = 0xFB, - MONSTER_TROLL_3 = 0xFC, - MONSTER_TREANT_1 = 0xFD, - MONSTER_TREANT_3 = 0xFF, - MONSTER_GHOUL_1 = 0x100, - MONSTER_GHOUL_3 = 0x102, +// TODO(captainurist): Rename to MonsterId? +// TODO(captainurist): codegen enum values. +enum class MONSTER_TYPE { + MONSTER_0 = 0, // TODO(captainurist): MONSTER_INVALID? + MONSTER_1 = 1, + MONSTER_DEVIL_1 = 22, + MONSTER_DEVIL_2 = 23, + MONSTER_DEVIL_3 = 24, + MONSTER_DRAGON_1 = 25, + MONSTER_DRAGON_2 = 26, + MONSTER_DRAGON_3 = 27, + MONSTER_45 = 45, + MONSTER_ELEMENTAL_WATER_1 = 46, + MONSTER_ELEMENTAL_WATER_3 = 48, + MONSTER_ELF_ARCHER_1 = 49, + MONSTER_ELF_ARCHER_3 = 51, + MONSTER_ELF_SPEARMAN_1 = 52, + MONSTER_ELF_SPEARMAN_3 = 54, + MONSTER_GHOST_1 = 70, + MONSTER_GHOST_3 = 72, + MONSTER_HARPY_1 = 85, + MONSTER_HARPY_2 = 86, + MONSTER_HARPY_3 = 87, + MONSTER_LICH_1 = 91, + MONSTER_LICH_3 = 93, + MONSTER_OOZE_1 = 112, + MONSTER_OOZE_2 = 113, + MONSTER_OOZE_3 = 114, + MONSTER_115 = 115, + MONSTER_PEASANT_ELF_FEMALE_1_1 = 133, + MONSTER_PEASANT_ELF_MALE_3_3 = 150, + MONSTER_186 = 186, + MONSTER_SKELETON_1 = 199, + MONSTER_SKELETON_3 = 201, + MONSTER_TITAN_1 = 211, + MONSTER_TITAN_3 = 213, + MONSTER_VAMPIRE_1 = 217, + MONSTER_VAMPIRE_3 = 219, + MONSTER_WIGHT_1 = 223, + MONSTER_WIGHT_3 = 225, + MONSTER_ZOMBIE_1 = 229, + MONSTER_ZOMBIE_3 = 231, + MONSTER_232 = 232, + MONSTER_PEASANT_GOBLIN_MALE_3_3 = 249, + MONSTER_TROLL_1 = 250, + MONSTER_TROLL_2 = 251, + MONSTER_TROLL_3 = 252, + MONSTER_TREANT_1 = 253, + MONSTER_TREANT_3 = 255, + MONSTER_GHOUL_1 = 256, + MONSTER_GHOUL_3 = 258, + + MONSTER_FIRST = MONSTER_1, + MONSTER_LAST = 277, + + MONSTER_FIRST_ARENA = MONSTER_1, + MONSTER_LAST_ARENA = MONSTER_GHOUL_3 }; +using enum MONSTER_TYPE; + +inline Segment allArenaMonsters() { + return {MONSTER_FIRST_ARENA, MONSTER_LAST_ARENA}; +} /* 335 */ -enum MONSTER_SPECIAL_ABILITY_TYPE { +enum class MONSTER_SPECIAL_ABILITY_TYPE { MONSTER_SPECIAL_ABILITY_NONE = 0x0, MONSTER_SPECIAL_ABILITY_SHOT = 0x1, MONSTER_SPECIAL_ABILITY_SUMMON = 0x2, MONSTER_SPECIAL_ABILITY_EXPLODE = 0x3, }; +using enum MONSTER_SPECIAL_ABILITY_TYPE; -enum MONSTER_MOVEMENT_TYPE { +enum class MONSTER_MOVEMENT_TYPE { MONSTER_MOVEMENT_TYPE_SHORT = 0x0, MONSTER_MOVEMENT_TYPE_MEDIUM = 0x1, MONSTER_MOVEMENT_TYPE_LONG = 0x2, @@ -79,9 +100,10 @@ enum MONSTER_MOVEMENT_TYPE { MONSTER_MOVEMENT_TYPE_FREE = 0x4, MONSTER_MOVEMENT_TYPE_STATIONARY = 0x5, }; +using enum MONSTER_MOVEMENT_TYPE; /* 336 */ -enum MONSTER_SUPERTYPE { +enum class MONSTER_SUPERTYPE { MONSTER_SUPERTYPE_UNDEAD = 0x1, MONSTER_SUPERTYPE_KREEGAN = 0x2, MONSTER_SUPERTYPE_DRAGON = 0x3, @@ -91,8 +113,9 @@ enum MONSTER_SUPERTYPE { MONSTER_SUPERTYPE_TITAN = 0x7, MONSTER_SUPERTYPE_8 = 0x8, }; +using enum MONSTER_SUPERTYPE; -enum SPECIAL_ATTACK_TYPE : uint8_t { +enum class SPECIAL_ATTACK_TYPE : uint8_t { SPECIAL_ATTACK_NONE = 0, SPECIAL_ATTACK_CURSE = 1, SPECIAL_ATTACK_WEAK = 2, @@ -118,15 +141,18 @@ enum SPECIAL_ATTACK_TYPE : uint8_t { SPECIAL_ATTACK_MANA_DRAIN = 22, SPECIAL_ATTACK_FEAR = 23, }; +using enum SPECIAL_ATTACK_TYPE; struct MonsterInfo { - enum HostilityRadius { + // TODO(captainurist): move into global namespace. + enum class HostilityRadius { Hostility_Friendly = 0, Hostility_Close = 1, Hostility_Short = 2, Hostility_Medium = 3, Hostility_Long = 4 }; + using enum HostilityRadius; std::string pName; std::string pPictureName; @@ -135,9 +161,9 @@ struct MonsterInfo { uint8_t uTreasureDiceRolls = 0; uint8_t uTreasureDiceSides = 0; ItemTreasureLevel uTreasureLevel = ITEM_TREASURE_LEVEL_INVALID; - uint8_t uTreasureType = 0; + RandomItemType uTreasureType = RANDOM_ITEM_ANY; uint8_t uFlying = 0; - uint8_t uMovementType = 0; + MONSTER_MOVEMENT_TYPE uMovementType = MONSTER_MOVEMENT_TYPE_SHORT; uint8_t uAIType = 0; HostilityRadius uHostilityType = Hostility_Friendly; SPECIAL_ATTACK_TYPE uSpecialAttackType = SPECIAL_ATTACK_NONE; @@ -167,15 +193,12 @@ struct MonsterInfo { uint8_t uResLight = 0; uint8_t uResDark = 0; uint8_t uResPhysical = 0; - uint8_t uSpecialAbilityType = 0; // 0 SPECIAL_ABILITY_TYPE_NONE - // 1 SPECIAL_ABILITY_TYPE_SHOT - // 2 SPECIAL_ABILITY_TYPE_SUMMON - // 3 SPECIAL_ABILITY_TYPE_EXPLODE + MONSTER_SPECIAL_ABILITY_TYPE uSpecialAbilityType = MONSTER_SPECIAL_ABILITY_NONE; uint8_t uSpecialAbilityDamageDiceRolls = 0; uint8_t uSpecialAbilityDamageDiceSides = 0; uint8_t uSpecialAbilityDamageDiceBonus = 0; uint8_t uNumCharactersAttackedPerSpecialAbility = 0; - uint16_t uID = 0; + MONSTER_TYPE uID = MONSTER_0; bool bBloodSplatOnDeath = 0; // true for bloodsplat on death CombinedSkillValue uSpellSkillAndMastery1; CombinedSkillValue uSpellSkillAndMastery2; @@ -192,14 +215,14 @@ struct MonsterInfo { struct MonsterStats { void Initialize(const Blob &monsters); void InitializePlacements(const Blob &placements); - signed int FindMonsterByTextureName(const std::string &Str2); + MONSTER_TYPE FindMonsterByTextureName(const std::string &Str2); - static bool BelongsToSupertype(unsigned int uMonsterInfoID, - enum MONSTER_SUPERTYPE eSupertype); + static bool BelongsToSupertype(MONSTER_TYPE uMonsterInfoID, + MONSTER_SUPERTYPE eSupertype); - MonsterInfo pInfos[265]; // 0 - 5b18h + IndexedArray pInfos; // 0 - 5b18h std::array pPlaceStrings; // 5B18h placement counts from 1 - unsigned int uNumMonsters; // 5B94h + unsigned int uNumMonsters; // 5B94h // TODO(captainurist): can drop? unsigned int uNumPlacements; // 5B98h int field_5B9C; }; @@ -216,10 +239,10 @@ struct MonsterDesc { }; struct MonsterList { - int16_t GetMonsterIDByName(const std::string &pMonsterName); + MONSTER_TYPE GetMonsterIDByName(const std::string &pMonsterName); bool FromFileTxt(const char *Args); - std::vector pMonsters; + IndexedArray pMonsters; }; extern struct MonsterStats *pMonsterStats; diff --git a/src/Engine/Objects/SpriteObject.cpp b/src/Engine/Objects/SpriteObject.cpp index 2242c2d9df2d..ad6537bb7a45 100644 --- a/src/Engine/Objects/SpriteObject.cpp +++ b/src/Engine/Objects/SpriteObject.cpp @@ -243,7 +243,7 @@ void SpriteObject::updateObjectODM(unsigned int uLayingItemID) { // TODO: why pActors.size() - 1? Should just check for .size() if ((actorId >= 0) && (actorId < (pActors.size() - 1))) { for (int j = 0; j < pActors.size(); ++j) { - if (pActors[actorId].GetActorsRelation(&pActors[j])) { + if (pActors[actorId].GetActorsRelation(&pActors[j]) != MonsterInfo::Hostility_Friendly) { CollideWithActor(j, 0); } } @@ -402,8 +402,8 @@ void SpriteObject::updateObjectBLV(unsigned int uLayingItemID) { // not sure: // pMonsterList->pMonsters[v39b->word_000086_some_monster_id-1].uToHitRadius int radius = 0; - if (pActors[actloop].word_000086_some_monster_id) { // not always filled in from scripted monsters - radius = pMonsterList->pMonsters[pActors[actloop].word_000086_some_monster_id - 1].uToHitRadius; + if (pActors[actloop].word_000086_some_monster_id != MONSTER_0) { // not always filled in from scripted monsters + radius = pMonsterList->pMonsters[pActors[actloop].word_000086_some_monster_id].uToHitRadius; } CollideWithActor(actloop, radius); } @@ -724,7 +724,7 @@ bool processSpellImpact(unsigned int uLayingItemID, Pid pid) { ObjectDesc *objectDesc = &pObjectList->pObjects[object->uObjectDescID]; if (pid.type() == OBJECT_Actor) { - if (object->spell_caster_pid.type() == OBJECT_Actor && !pActors[object->spell_caster_pid.id()].GetActorsRelation(&pActors[pid.id()])) { + if (object->spell_caster_pid.type() == OBJECT_Actor && pActors[object->spell_caster_pid.id()].GetActorsRelation(&pActors[pid.id()]) == MonsterInfo::Hostility_Friendly) { return 1; } } else { diff --git a/src/Engine/Party.cpp b/src/Engine/Party.cpp index e5d1bab95835..f751252d9058 100644 --- a/src/Engine/Party.cpp +++ b/src/Engine/Party.cpp @@ -121,7 +121,7 @@ void Party::Zero() { uNumDeaths = 0; uNumPrisonTerms = 0; uNumBountiesCollected = 0; - monster_id_for_hunting.fill(0); + monster_id_for_hunting.fill(MONSTER_0); monster_for_hunting_killed.fill(false); days_played_without_rest = 0; _questBits.reset(); @@ -507,7 +507,7 @@ void Party::createDefaultParty(bool bDebugGiveItems) { if (bDebugGiveItems) { Dst.Reset(); - pItemTable->generateItem(ITEM_TREASURE_LEVEL_2, 40, &Dst); // ring + pItemTable->generateItem(ITEM_TREASURE_LEVEL_2, RANDOM_ITEM_RING, &Dst); pCharacter.AddItem2(-1, &Dst); for (CharacterSkillType skill : allVisibleSkills()) { if (pCharacter.pActiveSkills[skill]) { diff --git a/src/Engine/Party.h b/src/Engine/Party.h index a385998bbf1e..cd05673d7157 100644 --- a/src/Engine/Party.h +++ b/src/Engine/Party.h @@ -306,7 +306,7 @@ struct Party { unsigned int uNumDeaths; int uNumPrisonTerms; unsigned int uNumBountiesCollected; - IndexedArray monster_id_for_hunting; + IndexedArray monster_id_for_hunting; IndexedArray monster_for_hunting_killed; unsigned char days_played_without_rest; IndexedBitset _questBits; diff --git a/src/Engine/Snapshots/EntitySnapshots.cpp b/src/Engine/Snapshots/EntitySnapshots.cpp index f8d577e2dc4a..960691a405d5 100644 --- a/src/Engine/Snapshots/EntitySnapshots.cpp +++ b/src/Engine/Snapshots/EntitySnapshots.cpp @@ -140,6 +140,14 @@ static void reconstruct(const uint16_t &src, CombinedSkillValue *dst) { *dst = CombinedSkillValue::fromJoined(src); } +static void snapshot(const MONSTER_TYPE &src, int16_t *dst) { + *dst = std::to_underlying(src); +} + +static void reconstruct(int16_t src, MONSTER_TYPE *dst) { + *dst = static_cast(src); +} + static void snapshot(const BBoxi &src, BBoxs *dst) { // TODO(captainurist): do we need to check for overflows here? dst->x1 = src.x1; @@ -1142,12 +1150,12 @@ void snapshot(const Actor &src, Actor_MM7 *dst) { dst->pMonsterInfo.treasureDiceRolls = src.monsterInfo.uTreasureDiceRolls; dst->pMonsterInfo.treasureDiceSides = src.monsterInfo.uTreasureDiceSides; dst->pMonsterInfo.treasureLevel = std::to_underlying(src.monsterInfo.uTreasureLevel); - dst->pMonsterInfo.treasureType = src.monsterInfo.uTreasureType; + dst->pMonsterInfo.treasureType = std::to_underlying(src.monsterInfo.uTreasureType); dst->pMonsterInfo.flying = src.monsterInfo.uFlying; - dst->pMonsterInfo.movementType = src.monsterInfo.uMovementType; + dst->pMonsterInfo.movementType = std::to_underlying(src.monsterInfo.uMovementType); dst->pMonsterInfo.aiType = src.monsterInfo.uAIType; dst->pMonsterInfo.hostilityType = std::to_underlying(src.monsterInfo.uHostilityType); - dst->pMonsterInfo.specialAttackType = src.monsterInfo.uSpecialAttackType; + dst->pMonsterInfo.specialAttackType = std::to_underlying(src.monsterInfo.uSpecialAttackType); dst->pMonsterInfo.specialAttackLevel = src.monsterInfo.uSpecialAttackLevel; dst->pMonsterInfo.attack1Type = std::to_underlying(src.monsterInfo.uAttack1Type); dst->pMonsterInfo.attack1DamageDiceRolls = src.monsterInfo.uAttack1DamageDiceRolls; @@ -1174,12 +1182,12 @@ void snapshot(const Actor &src, Actor_MM7 *dst) { dst->pMonsterInfo.resLight = src.monsterInfo.uResLight; dst->pMonsterInfo.resDark = src.monsterInfo.uResDark; dst->pMonsterInfo.resPhysical = src.monsterInfo.uResPhysical; - dst->pMonsterInfo.specialAbilityType = src.monsterInfo.uSpecialAbilityType; + dst->pMonsterInfo.specialAbilityType = std::to_underlying(src.monsterInfo.uSpecialAbilityType); dst->pMonsterInfo.specialAbilityDamageDiceRolls = src.monsterInfo.uSpecialAbilityDamageDiceRolls; dst->pMonsterInfo.specialAbilityDamageDiceSides = src.monsterInfo.uSpecialAbilityDamageDiceSides; dst->pMonsterInfo.specialAbilityDamageDiceBonus = src.monsterInfo.uSpecialAbilityDamageDiceBonus; dst->pMonsterInfo.numCharactersAttackedPerSpecialAbility = src.monsterInfo.uNumCharactersAttackedPerSpecialAbility; - dst->pMonsterInfo.id = src.monsterInfo.uID; + dst->pMonsterInfo.id = std::to_underlying(src.monsterInfo.uID); dst->pMonsterInfo.bloodSplatOnDeath = src.monsterInfo.bBloodSplatOnDeath; snapshot(src.monsterInfo.uSpellSkillAndMastery1, &dst->pMonsterInfo.spellSkillAndMastery1); snapshot(src.monsterInfo.uSpellSkillAndMastery2, &dst->pMonsterInfo.spellSkillAndMastery2); @@ -1192,7 +1200,7 @@ void snapshot(const Actor &src, Actor_MM7 *dst) { dst->pMonsterInfo.recoveryTime = src.monsterInfo.uRecoveryTime; dst->pMonsterInfo.attackPreference = src.monsterInfo.uAttackPreference; dst->word_000084_range_attack = src.word_000084_range_attack; - dst->word_000086_some_monster_id = src.word_000086_some_monster_id; // base monster class monsterlist id + dst->word_000086_some_monster_id = std::to_underlying(src.word_000086_some_monster_id); // base monster class monsterlist id dst->uActorRadius = src.radius; dst->uActorHeight = src.height; dst->uMovementSpeed = src.moveSpeed; @@ -1236,9 +1244,9 @@ void reconstruct(const Actor_MM7 &src, Actor *dst) { dst->monsterInfo.uTreasureDiceRolls = src.pMonsterInfo.treasureDiceRolls; dst->monsterInfo.uTreasureDiceSides = src.pMonsterInfo.treasureDiceSides; dst->monsterInfo.uTreasureLevel = static_cast(src.pMonsterInfo.treasureLevel); - dst->monsterInfo.uTreasureType = src.pMonsterInfo.treasureType; + dst->monsterInfo.uTreasureType = static_cast(src.pMonsterInfo.treasureType); dst->monsterInfo.uFlying = src.pMonsterInfo.flying; - dst->monsterInfo.uMovementType = src.pMonsterInfo.movementType; + dst->monsterInfo.uMovementType = static_cast(src.pMonsterInfo.movementType); dst->monsterInfo.uAIType = src.pMonsterInfo.aiType; dst->monsterInfo.uHostilityType = static_cast(src.pMonsterInfo.hostilityType); dst->monsterInfo.uSpecialAttackType = static_cast(src.pMonsterInfo.specialAttackType); @@ -1268,12 +1276,12 @@ void reconstruct(const Actor_MM7 &src, Actor *dst) { dst->monsterInfo.uResLight = src.pMonsterInfo.resLight; dst->monsterInfo.uResDark = src.pMonsterInfo.resDark; dst->monsterInfo.uResPhysical = src.pMonsterInfo.resPhysical; - dst->monsterInfo.uSpecialAbilityType = src.pMonsterInfo.specialAbilityType; + dst->monsterInfo.uSpecialAbilityType = static_cast(src.pMonsterInfo.specialAbilityType); dst->monsterInfo.uSpecialAbilityDamageDiceRolls = src.pMonsterInfo.specialAbilityDamageDiceRolls; dst->monsterInfo.uSpecialAbilityDamageDiceSides = src.pMonsterInfo.specialAbilityDamageDiceSides; dst->monsterInfo.uSpecialAbilityDamageDiceBonus = src.pMonsterInfo.specialAbilityDamageDiceBonus; dst->monsterInfo.uNumCharactersAttackedPerSpecialAbility = src.pMonsterInfo.numCharactersAttackedPerSpecialAbility; - dst->monsterInfo.uID = src.pMonsterInfo.id; + dst->monsterInfo.uID = static_cast(src.pMonsterInfo.id); dst->monsterInfo.bBloodSplatOnDeath = src.pMonsterInfo.bloodSplatOnDeath; reconstruct(src.pMonsterInfo.spellSkillAndMastery1, &dst->monsterInfo.uSpellSkillAndMastery1); reconstruct(src.pMonsterInfo.spellSkillAndMastery2, &dst->monsterInfo.uSpellSkillAndMastery2); @@ -1286,7 +1294,7 @@ void reconstruct(const Actor_MM7 &src, Actor *dst) { dst->monsterInfo.uRecoveryTime = src.pMonsterInfo.recoveryTime; dst->monsterInfo.uAttackPreference = src.pMonsterInfo.attackPreference; dst->word_000084_range_attack = src.word_000084_range_attack; - dst->word_000086_some_monster_id = src.word_000086_some_monster_id; // base monster class monsterlist id + dst->word_000086_some_monster_id = static_cast(src.word_000086_some_monster_id); // base monster class monsterlist id dst->radius = src.uActorRadius; dst->height = src.uActorHeight; dst->moveSpeed = src.uMovementSpeed; @@ -1684,7 +1692,7 @@ void snapshot(const LocationTime &src, LocationTime_MM7 *dst) { snapshot(src.last_visit, &dst->last_visit); snapshot(src.sky_texture_name, &dst->sky_texture_name); - dst->day_attrib = src.day_attrib; + dst->day_attrib = std::to_underlying(src.day_attrib); dst->day_fogrange_1 = src.day_fogrange_1; dst->day_fogrange_2 = src.day_fogrange_2; } @@ -1692,7 +1700,7 @@ void snapshot(const LocationTime &src, LocationTime_MM7 *dst) { void reconstruct(const LocationTime_MM7 &src, LocationTime *dst) { reconstruct(src.last_visit, &dst->last_visit); reconstruct(src.sky_texture_name, &dst->sky_texture_name); - dst->day_attrib = src.day_attrib; + dst->day_attrib = static_cast(src.day_attrib); dst->day_fogrange_1 = src.day_fogrange_1; dst->day_fogrange_2 = src.day_fogrange_2; } diff --git a/src/Engine/Snapshots/TableSerialization.cpp b/src/Engine/Snapshots/TableSerialization.cpp index 0078dd50e661..713b83bc70ce 100644 --- a/src/Engine/Snapshots/TableSerialization.cpp +++ b/src/Engine/Snapshots/TableSerialization.cpp @@ -1,5 +1,7 @@ #include "TableSerialization.h" +#include + #include "Engine/Tables/CharacterFrameTable.h" #include "Engine/Tables/IconFrameTable.h" #include "Engine/Tables/TileTable.h" @@ -66,14 +68,23 @@ void deserialize(const TriBlob &src, IconFrameTable *dst) { } void deserialize(const TriBlob &src, MonsterList *dst) { - dst->pMonsters.clear(); + std::vector monsters; if (src.mm6) - deserialize(src.mm6, &dst->pMonsters, tags::append, tags::via); + deserialize(src.mm6, &monsters, tags::append, tags::via); if (src.mm7) - deserialize(src.mm7, &dst->pMonsters, tags::append, tags::via); + deserialize(src.mm7, &monsters, tags::append, tags::via); if (src.mm8) - deserialize(src.mm8, &dst->pMonsters, tags::append, tags::via); + deserialize(src.mm8, &monsters, tags::append, tags::via); + + assert(monsters.size() <= dst->pMonsters.size()); // TODO(captainurist): this shouldn't be an assertion. + + dst->pMonsters.fill(MonsterDesc()); + for (size_t i = 0; MONSTER_TYPE index : dst->pMonsters.indices()) { + if (i >= monsters.size()) + break; + dst->pMonsters[index] = monsters[i++]; + } assert(!dst->pMonsters.empty()); } diff --git a/src/Engine/Tables/FactionTable.cpp b/src/Engine/Tables/FactionTable.cpp index fd3b32a4edb6..bd3bdfabd6ed 100644 --- a/src/Engine/Tables/FactionTable.cpp +++ b/src/Engine/Tables/FactionTable.cpp @@ -36,7 +36,7 @@ void FactionTable::Initialize(const Blob &factions) { *tmp_pos = 0; if (temp_str_len) { if (decode_step >= 1 && decode_step < 90) - relations[decode_step - 1][i] = atoi(test_string); + relations[decode_step - 1][i] = static_cast(atoi(test_string)); } else { break_loop = true; } diff --git a/src/Engine/Tables/FactionTable.h b/src/Engine/Tables/FactionTable.h index 367315e2e747..82019f1a86e2 100644 --- a/src/Engine/Tables/FactionTable.h +++ b/src/Engine/Tables/FactionTable.h @@ -1,11 +1,13 @@ #pragma once +#include "Engine/Objects/Monsters.h" // TODO(captainurist): MonsterEnums.h + class Blob; struct FactionTable { void Initialize(const Blob &factions); - char relations[89][89]; + MonsterInfo::HostilityRadius relations[89][89]; // TODO(captainurist): index is 1 + MONSTER_TYPE / 3? }; extern FactionTable *pFactionTable; diff --git a/src/Engine/Tables/FrameTableInc.h b/src/Engine/Tables/FrameTableInc.h index e07e3967bdc3..9f35a07c0a1f 100644 --- a/src/Engine/Tables/FrameTableInc.h +++ b/src/Engine/Tables/FrameTableInc.h @@ -1,6 +1,6 @@ #pragma once -/* 322 */ +// TODO(captainurist): #enum where is this used? enum FRAME_TABLE_FLAGS { FRAME_TABLE_MORE_FRAMES = 0x1, FRAME_TABLE_FIRST = 0x4, diff --git a/src/Engine/Tables/ItemTable.cpp b/src/Engine/Tables/ItemTable.cpp index 1d273def7bd3..bd3f1005fefa 100644 --- a/src/Engine/Tables/ItemTable.cpp +++ b/src/Engine/Tables/ItemTable.cpp @@ -365,7 +365,7 @@ void ItemTable::LoadPotionNotes(const Blob &potionNotes) { } } -void ItemTable::generateItem(ItemTreasureLevel treasure_level, unsigned int uTreasureType, ItemGen *outItem) { +void ItemTable::generateItem(ItemTreasureLevel treasure_level, RandomItemType uTreasureType, ItemGen *outItem) { assert(isRandomTreasureLevel(treasure_level)); int current_chance; // ebx@43 @@ -382,97 +382,95 @@ void ItemTable::generateItem(ItemTreasureLevel treasure_level, unsigned int uTre if (!outItem) outItem = (ItemGen*)malloc(sizeof(ItemGen)); memset(outItem, 0, sizeof(*outItem)); - if (uTreasureType) { // generate known treasure type + if (uTreasureType != RANDOM_ITEM_ANY) { // generate known treasure type ITEM_EQUIP_TYPE requested_equip; CharacterSkillType requested_skill = CHARACTER_SKILL_INVALID; - // TODO(captainurist): enum! - // See https://github.com/GrayFace/MMExtension/blob/4d6600f164315f38157591d7f0307a86594c22ef/Scripts/Core/ConstAndBits.lua#L592 switch (uTreasureType) { - case 20: + case RANDOM_ITEM_WEAPON: requested_equip = EQUIP_SINGLE_HANDED; break; - case 21: + case RANDOM_ITEM_ARMOR: requested_equip = EQUIP_ARMOUR; break; - case 22: + case RANDOM_ITEM_MICS: requested_skill = CHARACTER_SKILL_MISC; break; - case 23: + case RANDOM_ITEM_SWORD: requested_skill = CHARACTER_SKILL_SWORD; break; - case 24: + case RANDOM_ITEM_DAGGER: requested_skill = CHARACTER_SKILL_DAGGER; break; - case 25: + case RANDOM_ITEM_AXE: requested_skill = CHARACTER_SKILL_AXE; break; - case 26: + case RANDOM_ITEM_SPEAR: requested_skill = CHARACTER_SKILL_SPEAR; break; - case 27: + case RANDOM_ITEM_BOW: requested_skill = CHARACTER_SKILL_BOW; break; - case 28: + case RANDOM_ITEM_MACE: requested_skill = CHARACTER_SKILL_MACE; break; - case 29: + case RANDOM_ITEM_CLUB: requested_skill = CHARACTER_SKILL_CLUB; break; - case 30: + case RANDOM_ITEM_STAFF: requested_skill = CHARACTER_SKILL_STAFF; break; - case 31: + case RANDOM_ITEM_LEATHER_ARMOR: requested_skill = CHARACTER_SKILL_LEATHER; break; - case 32: + case RANDOM_ITEM_CHAIN_ARMOR: requested_skill = CHARACTER_SKILL_CHAIN; break; - case 33: + case RANDOM_ITEM_PLATE_ARMOR: requested_skill = CHARACTER_SKILL_PLATE; break; - case 34: + case RANDOM_ITEM_SHIELD: requested_equip = EQUIP_SHIELD; break; - case 35: + case RANDOM_ITEM_HELMET: requested_equip = EQUIP_HELMET; break; - case 36: + case RANDOM_ITEM_BELT: requested_equip = EQUIP_BELT; break; - case 37: + case RANDOM_ITEM_CLOAK: requested_equip = EQUIP_CLOAK; break; - case 38: + case RANDOM_ITEM_GAUNTLETS: requested_equip = EQUIP_GAUNTLETS; break; - case 39: + case RANDOM_ITEM_BOOTS: requested_equip = EQUIP_BOOTS; break; - case 40: + case RANDOM_ITEM_RING: requested_equip = EQUIP_RING; break; - case 41: + case RANDOM_ITEM_AMULET: requested_equip = EQUIP_AMULET; break; - case 42: + case RANDOM_ITEM_WAND: requested_equip = EQUIP_WAND; break; - case 43: + case RANDOM_ITEM_SPELL_SCROLL: requested_equip = EQUIP_SPELL_SCROLL; break; - case 44: + case RANDOM_ITEM_POTION: requested_equip = EQUIP_POTION; break; - case 45: + case RANDOM_ITEM_REAGENT: requested_equip = EQUIP_REAGENT; break; - case 46: + case RANDOM_ITEM_GEM: requested_equip = EQUIP_GEM; break; default: __debugbreak(); // check this condition // TODO(captainurist): explore - requested_equip = (ITEM_EQUIP_TYPE)(uTreasureType - 1); + requested_equip = static_cast(std::to_underlying(uTreasureType) - 1); break; } spawnableRequestedItems.fill(ITEM_NULL); diff --git a/src/Engine/Tables/ItemTable.h b/src/Engine/Tables/ItemTable.h index 2dcec7a080b7..b55f5c5147d0 100644 --- a/src/Engine/Tables/ItemTable.h +++ b/src/Engine/Tables/ItemTable.h @@ -23,7 +23,7 @@ struct ItemTable { /** * @offset 0x456620 */ - void generateItem(ItemTreasureLevel treasure_level, unsigned int uTreasureType, ItemGen *pItem); + void generateItem(ItemTreasureLevel treasure_level, RandomItemType uTreasureType, ItemGen *pItem); void SetSpecialBonus(ItemGen *pItem); bool IsMaterialSpecial(const ItemGen *pItem); bool IsMaterialNonCommon(const ItemGen *pItem); diff --git a/src/Engine/Tables/NPCTable.cpp b/src/Engine/Tables/NPCTable.cpp index 9c69d82fa16b..860d820b7e40 100644 --- a/src/Engine/Tables/NPCTable.cpp +++ b/src/Engine/Tables/NPCTable.cpp @@ -464,7 +464,7 @@ void NPCStats::InitializeNPCProfs(const Blob &npcProfs) { } //----- (0047732C) -------------------------------------------------------- -void NPCStats::InitializeAdditionalNPCs(NPCData *pNPCDataBuff, int npc_uid, +void NPCStats::InitializeAdditionalNPCs(NPCData *pNPCDataBuff, MONSTER_TYPE npc_uid, HOUSE_ID uLocation2D, MapId uMapId) { int rep_gen; int uGeneratedPortret; // ecx@23 @@ -571,7 +571,8 @@ void NPCStats::InitializeAdditionalNPCs(NPCData *pNPCDataBuff, int npc_uid, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0}; - uint8_t seed = (uint8_t)((double)(npc_uid - 1) / 3.0); + // TODO(captainurist): encapsulate enum arithmetic. + uint8_t seed = (uint8_t)((std::to_underlying(npc_uid) - 1) / 3.0); CharacterSex uNPCSex = NPCSexGenTable[seed]; uRace = NPCRaceGenTable[seed]; pNPCDataBuff->uSex = uNPCSex; diff --git a/src/Engine/Tables/NPCTable.h b/src/Engine/Tables/NPCTable.h index c56e54df61ef..e93c754e9936 100644 --- a/src/Engine/Tables/NPCTable.h +++ b/src/Engine/Tables/NPCTable.h @@ -5,6 +5,7 @@ #include "Engine/Objects/NPCEnums.h" #include "Engine/Objects/CharacterEnums.h" +#include "Engine/Objects/Monsters.h" // TODO(captainurist): MonsterEnums.h #include "Engine/MapEnums.h" #include "GUI/UI/UIHouseEnums.h" @@ -90,7 +91,7 @@ struct NPCStats { void InitializeNPCGreets(const Blob &npcGreets); void InitializeNPCGroups(const Blob &npcGroups); void InitializeNPCNews(const Blob &npcNews); - void InitializeAdditionalNPCs(NPCData *pNPCDataBuff, int npc_uid, + void InitializeAdditionalNPCs(NPCData *pNPCDataBuff, MONSTER_TYPE npc_uid, HOUSE_ID uLocation2D, MapId uMapId); /** * @offset 0x476C60 diff --git a/src/Engine/TurnEngine/TurnEngine.cpp b/src/Engine/TurnEngine/TurnEngine.cpp index 4696fd7ada62..bff9f8ea3af4 100644 --- a/src/Engine/TurnEngine/TurnEngine.cpp +++ b/src/Engine/TurnEngine/TurnEngine.cpp @@ -253,7 +253,7 @@ void stru262_TurnBased::AITurnBasedAction() { pActors[i].buffs[j].IsBuffExpiredToTime(pParty->GetPlayingTime()); if (pActors[i].buffs[ACTOR_BUFF_SHRINK].Expired()) { - pActors[i].height = pMonsterList->pMonsters[pActors[i].monsterInfo.uID - 1].uMonsterHeight; + pActors[i].height = pMonsterList->pMonsters[pActors[i].monsterInfo.uID].uMonsterHeight; pActors[i].buffs[ACTOR_BUFF_SHRINK].Reset(); } @@ -641,7 +641,7 @@ void stru262_TurnBased::AI_Action_(int queue_index) { unsigned int actor_id; // edi@2 AIDirection v7; // esi@10 int v9; // ecx@10 - signed int v10; // eax@13 + MonsterInfo::HostilityRadius v10; // eax@13 ABILITY_INDEX v14; // eax@29 AIDirection a3; // [sp+Ch] [bp-44h]@10 AIDirection v18; // [sp+28h] [bp-28h]@10 @@ -658,7 +658,7 @@ void stru262_TurnBased::AI_Action_(int queue_index) { Actor::_SelectTarget(actor_id, &ai_near_actors_targets_pid[actor_id], true); v22 = ai_near_actors_targets_pid[actor_id]; - if (pActors[actor_id].monsterInfo.uHostilityType && !v22) + if (pActors[actor_id].monsterInfo.uHostilityType != MonsterInfo::Hostility_Friendly && !v22) pActors[actor_id].monsterInfo.uHostilityType = MonsterInfo::Hostility_Friendly; Actor::GetDirectionInfo(Pid(OBJECT_Actor, actor_id), v22, &v7, 0); @@ -671,36 +671,33 @@ void stru262_TurnBased::AI_Action_(int queue_index) { // (pMonsterStats->pInfos[pActors[v22.id()].pMonsterInfo.uID].uID // - 1) / 3] + (v5->pMonsterInfo.uID - 1) / 3); v10 = pFactionTable->relations - [(pMonsterStats - ->pInfos[pActors[v22.id()].monsterInfo.uID] - .uID) / - 3 + - 1][(pActors[actor_id].monsterInfo.uID - 1) / 3 + 1]; + [(std::to_underlying(pMonsterStats->pInfos[pActors[v22.id()].monsterInfo.uID].uID) - 1) / 3 + 1] // Was w/o -1 here, probably was a bug. + [(std::to_underlying(pActors[actor_id].monsterInfo.uID) - 1) / 3 + 1]; else - v10 = 4; + v10 = MonsterInfo::Hostility_Long; switch (v10) { - case 1: + case MonsterInfo::Hostility_Close: if ((double)(signed int)v9 < 307.2) pActors[actor_id].monsterInfo.uHostilityType = MonsterInfo::Hostility_Long; break; - case 2: + case MonsterInfo::Hostility_Short: if (v9 < 1024) pActors[actor_id].monsterInfo.uHostilityType = MonsterInfo::Hostility_Long; break; - case 3: + case MonsterInfo::Hostility_Medium: if (v9 < 2560) pActors[actor_id].monsterInfo.uHostilityType = MonsterInfo::Hostility_Long; break; - case 4: + case MonsterInfo::Hostility_Long: if (v9 < 5120) pActors[actor_id].monsterInfo.uHostilityType = MonsterInfo::Hostility_Long; break; } - if (pActors[actor_id].monsterInfo.uHostilityType == 4 && v22 && + if (pActors[actor_id].monsterInfo.uHostilityType == MonsterInfo::Hostility_Long && v22 && (signed int)v9 < 5120) { v14 = pActors[actor_id].special_ability_use_check(actor_id); pQueue[queue_index].AI_action_type = TE_AI_STAND; @@ -849,7 +846,7 @@ void stru262_TurnBased::ActorAIDoAdditionalMove() { bool stru262_TurnBased::ActorMove(signed int queue_position) { AIDirection v9; // esi@10 int v11; // ecx@10 - uint8_t pHostileType; // al@12 + MonsterInfo::HostilityRadius pHostileType; // al@12 AIDirection a3; // [sp+Ch] [bp-48h]@10 AIDirection pDir; // [sp+28h] [bp-2Ch]@10 unsigned int uActorID; // [sp+50h] [bp-4h]@2 @@ -863,7 +860,7 @@ bool stru262_TurnBased::ActorMove(signed int queue_position) { pActors[uActorID].aiState == Summoned) return 1; Actor::_SelectTarget(uActorID, &ai_near_actors_targets_pid[uActorID], true); - if (pActors[uActorID].monsterInfo.uHostilityType && + if (pActors[uActorID].monsterInfo.uHostilityType != MonsterInfo::Hostility_Friendly && !ai_near_actors_targets_pid[uActorID]) pActors[uActorID].monsterInfo.uHostilityType = MonsterInfo::Hostility_Friendly; @@ -875,17 +872,17 @@ bool stru262_TurnBased::ActorMove(signed int queue_position) { if (v11 < 0) v11 = 0; pHostileType = pActors[uActorID].monsterInfo.uHostilityType; switch (pHostileType) { - case 1: + case MonsterInfo::Hostility_Close: if ((double)v11 < 307.2) pActors[uActorID].monsterInfo.uHostilityType = MonsterInfo::Hostility_Long; break; - case 2: + case MonsterInfo::Hostility_Short: if (v11 < 1024) pActors[uActorID].monsterInfo.uHostilityType = MonsterInfo::Hostility_Long; break; - case 3: + case MonsterInfo::Hostility_Medium: if (v11 < 2560) pActors[uActorID].monsterInfo.uHostilityType = MonsterInfo::Hostility_Long; diff --git a/src/Engine/mm7_data.cpp b/src/Engine/mm7_data.cpp index 66721d635e04..9387f8812547 100644 --- a/src/Engine/mm7_data.cpp +++ b/src/Engine/mm7_data.cpp @@ -2434,7 +2434,7 @@ int bDialogueUI_InitializeActor_NPC_ID; std::array byte_5E4C15; int uDefaultTravelTime_ByFoot; -int day_attrib; +MapWeatherFlags day_attrib; int day_fogrange_1; // fog start dist int day_fogrange_2; // fog end dist MapId dword_6BE13C_uCurrentlyLoadedLocationID; diff --git a/src/Engine/mm7_data.h b/src/Engine/mm7_data.h index b4e8ed571635..b4d6b9b89ae3 100644 --- a/src/Engine/mm7_data.h +++ b/src/Engine/mm7_data.h @@ -106,7 +106,7 @@ extern int _5C35C0_force_party_death; extern int bDialogueUI_InitializeActor_NPC_ID; extern int uDefaultTravelTime_ByFoot; -extern int day_attrib; +extern MapWeatherFlags day_attrib; extern int day_fogrange_1; extern int day_fogrange_2; extern float fWalkSpeedMultiplier; diff --git a/src/GUI/GUIEnums.h b/src/GUI/GUIEnums.h index 75976579528f..00d8827e4266 100644 --- a/src/GUI/GUIEnums.h +++ b/src/GUI/GUIEnums.h @@ -308,6 +308,7 @@ enum class ScreenType { }; using enum ScreenType; +// TODO(captainurist): #enum class enum WindowType { WINDOW_null = 0, WINDOW_MainMenu = 1, @@ -373,3 +374,13 @@ enum WindowType { }; MM_DECLARE_SERIALIZATION_FUNCTIONS(WindowType) + +enum class ShopScreen { + SHOP_SCREEN_INVALID = 0, + SHOP_SCREEN_BUY = 2, + SHOP_SCREEN_SELL = 3, + SHOP_SCREEN_IDENTIFY = 4, + SHOP_SCREEN_REPAIR = 5, + SHOP_SCREEN_SELL_FOR_CHEAP = 6, // Sell for half the price, was used in MM6 general store, not used in MM7. +}; +using enum ShopScreen; diff --git a/src/GUI/GUIProgressBar.h b/src/GUI/GUIProgressBar.h index 89c447534949..dccbe0a81aa6 100644 --- a/src/GUI/GUIProgressBar.h +++ b/src/GUI/GUIProgressBar.h @@ -6,11 +6,12 @@ class GraphicsImage; class GUIProgressBar { public: - enum Type { + enum class Type { TYPE_None = 0, TYPE_Fullscreen = 1, TYPE_Box = 2 }; + using enum Type; bool Initialize(Type type); void Reset(uint8_t uMaxProgress); diff --git a/src/GUI/GUIWindow.cpp b/src/GUI/GUIWindow.cpp index 3282871fca44..da1890fc5158 100644 --- a/src/GUI/GUIWindow.cpp +++ b/src/GUI/GUIWindow.cpp @@ -858,7 +858,7 @@ Color GetSkillColor(CharacterClassType uPlayerClass, CharacterSkillType uPlayerS return ui_character_skillinfo_cant_learn; } -std::string BuildDialogueString(const std::string &str, uint8_t uPlayerID, ItemGen *a3, HOUSE_ID houseId, int shop_screen, GameTime *a6) { +std::string BuildDialogueString(const std::string &str, uint8_t uPlayerID, ItemGen *a3, HOUSE_ID houseId, ShopScreen shop_screen, GameTime *a6) { std::string v1; Character *pPlayer; // ebx@3 std::string pText; // esi@7 @@ -1008,16 +1008,16 @@ std::string BuildDialogueString(const std::string &str, uint8_t uPlayerID, ItemG case 25: // base prices v29 = PriceCalculator::baseItemBuyingPrice(a3->GetValue(), buildingTable[houseId].fPriceMultiplier); switch (shop_screen) { - case 3: + case SHOP_SCREEN_SELL: v29 = PriceCalculator::baseItemSellingPrice(a3->GetValue(), buildingTable[houseId].fPriceMultiplier); break; - case 4: + case SHOP_SCREEN_IDENTIFY: v29 = PriceCalculator::baseItemIdentifyPrice(buildingTable[houseId].fPriceMultiplier); break; - case 5: + case SHOP_SCREEN_REPAIR: v29 = PriceCalculator::baseItemRepairPrice(a3->GetValue(), buildingTable[houseId].fPriceMultiplier); break; - case 6: + case SHOP_SCREEN_SELL_FOR_CHEAP: v29 = PriceCalculator::baseItemSellingPrice(a3->GetValue(), buildingTable[houseId].fPriceMultiplier) / 2; break; } @@ -1027,19 +1027,19 @@ std::string BuildDialogueString(const std::string &str, uint8_t uPlayerID, ItemG case 27: // actual price v29 = PriceCalculator::itemBuyingPriceForPlayer(pPlayer, a3->GetValue(), buildingTable[houseId].fPriceMultiplier); - if (shop_screen == 3) { + if (shop_screen == SHOP_SCREEN_SELL) { v29 = PriceCalculator::itemSellingPriceForPlayer(pPlayer, *a3, buildingTable[houseId].fPriceMultiplier); v1 = fmt::format("{}", v29); result += v1; break; } - if (shop_screen != 4) { // TODO(captainurist): enums for shop screens - if (shop_screen == 5) { + if (shop_screen != SHOP_SCREEN_IDENTIFY) { + if (shop_screen == SHOP_SCREEN_REPAIR) { v29 = PriceCalculator::itemRepairPriceForPlayer( pPlayer, a3->GetValue(), buildingTable[houseId].fPriceMultiplier); } else { - if (shop_screen == 6) { + if (shop_screen == SHOP_SCREEN_SELL_FOR_CHEAP) { // TODO(captainurist): encapsulate this logic in PriceCalculator v29 = PriceCalculator::itemSellingPriceForPlayer(pPlayer, *a3, buildingTable[houseId].fPriceMultiplier) / 2; if (!v29) // cannot be 0 diff --git a/src/GUI/GUIWindow.h b/src/GUI/GUIWindow.h index b59b1138f6d4..8e3da5a2697a 100644 --- a/src/GUI/GUIWindow.h +++ b/src/GUI/GUIWindow.h @@ -39,6 +39,7 @@ class Character; class NPCData; class GraphicsImage; class Logger; +struct ItemGen; struct WindowData { WindowData() {} @@ -268,7 +269,7 @@ bool isHoldingMouseRightButton(); * @offset 0x495461 */ std::string BuildDialogueString(const std::string &str, uint8_t uPlayerID, - struct ItemGen *a3, HOUSE_ID houseId, int shop_screen, + ItemGen *a3 = nullptr, HOUSE_ID houseId = HOUSE_INVALID, ShopScreen shop_screen = SHOP_SCREEN_INVALID, GameTime *a6 = nullptr); diff --git a/src/GUI/UI/Books/JournalBook.cpp b/src/GUI/UI/Books/JournalBook.cpp index d50976f26d4f..42253c9b97fa 100644 --- a/src/GUI/UI/Books/JournalBook.cpp +++ b/src/GUI/UI/Books/JournalBook.cpp @@ -52,7 +52,7 @@ GUIWindow_JournalBook::GUIWindow_JournalBook() : _currentIdx(0), GUIWindow_Book( for (int i = 0; i < pParty->PartyTimes.HistoryEventTimes.size(); i++) { if (pParty->PartyTimes.HistoryEventTimes[i].Valid()) { if (!pStorylineText->StoreLine[i + 1].pText.empty()) { - std::string str = BuildDialogueString(pStorylineText->StoreLine[i + 1].pText, 0, 0, HOUSE_INVALID, 0, &pParty->PartyTimes.HistoryEventTimes[i]); + std::string str = BuildDialogueString(pStorylineText->StoreLine[i + 1].pText, 0, 0, HOUSE_INVALID, SHOP_SCREEN_INVALID, &pParty->PartyTimes.HistoryEventTimes[i]); int pTextHeight = assets->pFontBookOnlyShadow->CalcTextHeight(str, journal_window.uFrameWidth, 1); int pages = ((pTextHeight - (assets->pFontBookOnlyShadow->GetHeight() - 3)) / (signed int)journal_window.uFrameHeight) + 1; for (int j = 0; j < pages; ++j) { @@ -115,7 +115,7 @@ void GUIWindow_JournalBook::Update() { if (_journalIdx.size()) { std::string str = BuildDialogueString(pStorylineText->StoreLine[_journalIdx[_currentIdx]].pText, - 0, 0, HOUSE_INVALID, 0, &pParty->PartyTimes.HistoryEventTimes[_journalIdx[_currentIdx] - 1]); + 0, 0, HOUSE_INVALID, SHOP_SCREEN_INVALID, &pParty->PartyTimes.HistoryEventTimes[_journalIdx[_currentIdx] - 1]); std::string pStringOnPage = assets->pFontBookOnlyShadow->GetPageTop(str, &journal_window, 1, _journalEntryPage[_currentIdx]); journal_window.DrawText(assets->pFontBookOnlyShadow.get(), {1, 0}, ui_book_journal_text_color, pStringOnPage, journal_window.uFrameY + journal_window.uFrameHeight, ui_book_journal_text_shadow); diff --git a/src/GUI/UI/Houses/MagicGuild.cpp b/src/GUI/UI/Houses/MagicGuild.cpp index 854d8dfebc69..6be0f0d64c05 100644 --- a/src/GUI/UI/Houses/MagicGuild.cpp +++ b/src/GUI/UI/Houses/MagicGuild.cpp @@ -237,8 +237,8 @@ void GUIWindow_MagicGuild::buyBooksDialogue() { if (pt.x >= testpos && pt.x <= testpos + (shop_ui_items_in_store[testx]->width())) { if ((pt.y >= 90 && pt.y <= (90 + (shop_ui_items_in_store[testx]->height()))) || (pt.y >= 250 && pt.y <= (250 + (shop_ui_items_in_store[testx]->height())))) { - MerchantPhrase phrase = pParty->activeCharacter().SelectPhrasesTransaction(item, BUILDING_MAGIC_SHOP, houseId(), 2); - std::string str = BuildDialogueString(pMerchantsBuyPhrases[phrase], pParty->activeCharacterIndex() - 1, item, houseId(), 2); + MerchantPhrase phrase = pParty->activeCharacter().SelectPhrasesTransaction(item, BUILDING_MAGIC_SHOP, houseId(), SHOP_SCREEN_BUY); + std::string str = BuildDialogueString(pMerchantsBuyPhrases[phrase], pParty->activeCharacterIndex() - 1, item, houseId(), SHOP_SCREEN_BUY); int textHeight = assets->pFontArrus->CalcTextHeight(str, working_window.uFrameWidth, 0); working_window.DrawTitleText(assets->pFontArrus.get(), 0, (SIDE_TEXT_BOX_BODY_TEXT_HEIGHT - textHeight) / 2 + SIDE_TEXT_BOX_BODY_TEXT_OFFSET, colorTable.White, str, 3); return; diff --git a/src/GUI/UI/Houses/Shops.cpp b/src/GUI/UI/Houses/Shops.cpp index 087e611595fe..eff424cc21c5 100644 --- a/src/GUI/UI/Houses/Shops.cpp +++ b/src/GUI/UI/Houses/Shops.cpp @@ -36,114 +36,112 @@ struct ITEM_VARIATION { ItemTreasureLevel treasure_level; - std::array item_class; + std::array item_class; }; -// TODO(Nik-RE-dev): enumerate treasure types used in this file - -static constexpr IndexedArray weaponShopVariationStandart = {{ - {HOUSE_WEAPON_SHOP_EMERALD_ISLAND, { ITEM_TREASURE_LEVEL_1, { 23, 27, 20, 20 } }}, - {HOUSE_WEAPON_SHOP_HARMONDALE, { ITEM_TREASURE_LEVEL_1, { 23, 24, 28, 20 } }}, - {HOUSE_WEAPON_SHOP_ERATHIA, { ITEM_TREASURE_LEVEL_2, { 23, 24, 25, 20 } }}, - {HOUSE_WEAPON_SHOP_TULAREAN_FOREST, { ITEM_TREASURE_LEVEL_2, { 27, 27, 26, 26 } }}, - {HOUSE_WEAPON_SHOP_CELESTE, { ITEM_TREASURE_LEVEL_4, { 24, 30, 25, 27 } }}, - {HOUSE_WEAPON_SHOP_PIT, { ITEM_TREASURE_LEVEL_4, { 24, 30, 25, 27 } }}, - {HOUSE_WEAPON_SHOP_MOUNT_NIGHON, { ITEM_TREASURE_LEVEL_3, { 30, 24, 20, 20 } }}, - {HOUSE_WEAPON_SHOP_TATALIA_1, { ITEM_TREASURE_LEVEL_2, { 20, 20, 20, 20 } }}, - {HOUSE_WEAPON_SHOP_AVLEE, { ITEM_TREASURE_LEVEL_3, { 27, 27, 26, 26 } }}, - {HOUSE_WEAPON_SHOP_STONE_CITY, { ITEM_TREASURE_LEVEL_3, { 28, 28, 25, 25 } }}, - {HOUSE_WEAPON_SHOP_CASTLE_HARMONDALE, { ITEM_TREASURE_LEVEL_2, { 23, 23, 24, 24 } }}, - {HOUSE_WEAPON_SHOP_TATALIA_2, { ITEM_TREASURE_LEVEL_3, { 23, 23, 26, 26 } }}, - {HOUSE_13, { ITEM_TREASURE_LEVEL_2, { 30, 26, 26, 26 } }}, - {HOUSE_14, { ITEM_TREASURE_LEVEL_2, { 28, 25, 28, 29 } }} +static constexpr IndexedArray weaponShopVariationStandard = {{ + {HOUSE_WEAPON_SHOP_EMERALD_ISLAND, { ITEM_TREASURE_LEVEL_1, { RANDOM_ITEM_SWORD, RANDOM_ITEM_BOW, RANDOM_ITEM_WEAPON, RANDOM_ITEM_WEAPON } }}, + {HOUSE_WEAPON_SHOP_HARMONDALE, { ITEM_TREASURE_LEVEL_1, { RANDOM_ITEM_SWORD, RANDOM_ITEM_DAGGER, RANDOM_ITEM_MACE, RANDOM_ITEM_WEAPON } }}, + {HOUSE_WEAPON_SHOP_ERATHIA, { ITEM_TREASURE_LEVEL_2, { RANDOM_ITEM_SWORD, RANDOM_ITEM_DAGGER, RANDOM_ITEM_AXE, RANDOM_ITEM_WEAPON } }}, + {HOUSE_WEAPON_SHOP_TULAREAN_FOREST, { ITEM_TREASURE_LEVEL_2, { RANDOM_ITEM_BOW, RANDOM_ITEM_BOW, RANDOM_ITEM_SPEAR, RANDOM_ITEM_SPEAR } }}, + {HOUSE_WEAPON_SHOP_CELESTE, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_DAGGER, RANDOM_ITEM_STAFF, RANDOM_ITEM_AXE, RANDOM_ITEM_BOW } }}, + {HOUSE_WEAPON_SHOP_PIT, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_DAGGER, RANDOM_ITEM_STAFF, RANDOM_ITEM_AXE, RANDOM_ITEM_BOW } }}, + {HOUSE_WEAPON_SHOP_MOUNT_NIGHON, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_STAFF, RANDOM_ITEM_DAGGER, RANDOM_ITEM_WEAPON, RANDOM_ITEM_WEAPON } }}, + {HOUSE_WEAPON_SHOP_TATALIA_1, { ITEM_TREASURE_LEVEL_2, { RANDOM_ITEM_WEAPON, RANDOM_ITEM_WEAPON, RANDOM_ITEM_WEAPON, RANDOM_ITEM_WEAPON } }}, + {HOUSE_WEAPON_SHOP_AVLEE, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_BOW, RANDOM_ITEM_BOW, RANDOM_ITEM_SPEAR, RANDOM_ITEM_SPEAR } }}, + {HOUSE_WEAPON_SHOP_STONE_CITY, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_MACE, RANDOM_ITEM_MACE, RANDOM_ITEM_AXE, RANDOM_ITEM_AXE } }}, + {HOUSE_WEAPON_SHOP_CASTLE_HARMONDALE, { ITEM_TREASURE_LEVEL_2, { RANDOM_ITEM_SWORD, RANDOM_ITEM_SWORD, RANDOM_ITEM_DAGGER, RANDOM_ITEM_DAGGER } }}, + {HOUSE_WEAPON_SHOP_TATALIA_2, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_SWORD, RANDOM_ITEM_SWORD, RANDOM_ITEM_SPEAR, RANDOM_ITEM_SPEAR } }}, + {HOUSE_13, { ITEM_TREASURE_LEVEL_2, { RANDOM_ITEM_STAFF, RANDOM_ITEM_SPEAR, RANDOM_ITEM_SPEAR, RANDOM_ITEM_SPEAR } }}, + {HOUSE_14, { ITEM_TREASURE_LEVEL_2, { RANDOM_ITEM_MACE, RANDOM_ITEM_AXE, RANDOM_ITEM_MACE, RANDOM_ITEM_CLUB } }} }}; static constexpr IndexedArray weaponShopVariationSpecial = {{ - {HOUSE_WEAPON_SHOP_EMERALD_ISLAND, { ITEM_TREASURE_LEVEL_2, { 25, 30, 20, 20 } }}, - {HOUSE_WEAPON_SHOP_HARMONDALE, { ITEM_TREASURE_LEVEL_2, { 23, 24, 28, 20 } }}, - {HOUSE_WEAPON_SHOP_ERATHIA, { ITEM_TREASURE_LEVEL_3, { 23, 24, 25, 20 } }}, - {HOUSE_WEAPON_SHOP_TULAREAN_FOREST, { ITEM_TREASURE_LEVEL_3, { 27, 27, 26, 26 } }}, - {HOUSE_WEAPON_SHOP_CELESTE, { ITEM_TREASURE_LEVEL_5, { 23, 26, 28, 27 } }}, - {HOUSE_WEAPON_SHOP_PIT, { ITEM_TREASURE_LEVEL_5, { 23, 26, 28, 27 } }}, - {HOUSE_WEAPON_SHOP_MOUNT_NIGHON, { ITEM_TREASURE_LEVEL_4, { 30, 24, 20, 20 } }}, - {HOUSE_WEAPON_SHOP_TATALIA_1, { ITEM_TREASURE_LEVEL_3, { 20, 20, 20, 20 } }}, - {HOUSE_WEAPON_SHOP_AVLEE, { ITEM_TREASURE_LEVEL_4, { 27, 27, 26, 26 } }}, - {HOUSE_WEAPON_SHOP_STONE_CITY, { ITEM_TREASURE_LEVEL_4, { 28, 28, 25, 25 } }}, - {HOUSE_WEAPON_SHOP_CASTLE_HARMONDALE, { ITEM_TREASURE_LEVEL_4, { 23, 23, 24, 24 } }}, - {HOUSE_WEAPON_SHOP_TATALIA_2, { ITEM_TREASURE_LEVEL_4, { 24, 24, 27, 20 } }}, - {HOUSE_13, { ITEM_TREASURE_LEVEL_4, { 30, 26, 26, 26 } }}, - {HOUSE_14, { ITEM_TREASURE_LEVEL_4, { 28, 25, 28, 29 } }} + {HOUSE_WEAPON_SHOP_EMERALD_ISLAND, { ITEM_TREASURE_LEVEL_2, { RANDOM_ITEM_AXE, RANDOM_ITEM_STAFF, RANDOM_ITEM_WEAPON, RANDOM_ITEM_WEAPON } }}, + {HOUSE_WEAPON_SHOP_HARMONDALE, { ITEM_TREASURE_LEVEL_2, { RANDOM_ITEM_SWORD, RANDOM_ITEM_DAGGER, RANDOM_ITEM_MACE, RANDOM_ITEM_WEAPON } }}, + {HOUSE_WEAPON_SHOP_ERATHIA, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_SWORD, RANDOM_ITEM_DAGGER, RANDOM_ITEM_AXE, RANDOM_ITEM_WEAPON } }}, + {HOUSE_WEAPON_SHOP_TULAREAN_FOREST, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_BOW, RANDOM_ITEM_BOW, RANDOM_ITEM_SPEAR, RANDOM_ITEM_SPEAR } }}, + {HOUSE_WEAPON_SHOP_CELESTE, { ITEM_TREASURE_LEVEL_5, { RANDOM_ITEM_SWORD, RANDOM_ITEM_SPEAR, RANDOM_ITEM_MACE, RANDOM_ITEM_BOW } }}, + {HOUSE_WEAPON_SHOP_PIT, { ITEM_TREASURE_LEVEL_5, { RANDOM_ITEM_SWORD, RANDOM_ITEM_SPEAR, RANDOM_ITEM_MACE, RANDOM_ITEM_BOW } }}, + {HOUSE_WEAPON_SHOP_MOUNT_NIGHON, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_STAFF, RANDOM_ITEM_DAGGER, RANDOM_ITEM_WEAPON, RANDOM_ITEM_WEAPON } }}, + {HOUSE_WEAPON_SHOP_TATALIA_1, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_WEAPON, RANDOM_ITEM_WEAPON, RANDOM_ITEM_WEAPON, RANDOM_ITEM_WEAPON } }}, + {HOUSE_WEAPON_SHOP_AVLEE, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_BOW, RANDOM_ITEM_BOW, RANDOM_ITEM_SPEAR, RANDOM_ITEM_SPEAR } }}, + {HOUSE_WEAPON_SHOP_STONE_CITY, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_MACE, RANDOM_ITEM_MACE, RANDOM_ITEM_AXE, RANDOM_ITEM_AXE } }}, + {HOUSE_WEAPON_SHOP_CASTLE_HARMONDALE, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_SWORD, RANDOM_ITEM_SWORD, RANDOM_ITEM_DAGGER, RANDOM_ITEM_DAGGER } }}, + {HOUSE_WEAPON_SHOP_TATALIA_2, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_DAGGER, RANDOM_ITEM_DAGGER, RANDOM_ITEM_BOW, RANDOM_ITEM_WEAPON } }}, + {HOUSE_13, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_STAFF, RANDOM_ITEM_SPEAR, RANDOM_ITEM_SPEAR, RANDOM_ITEM_SPEAR } }}, + {HOUSE_14, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_MACE, RANDOM_ITEM_AXE, RANDOM_ITEM_MACE, RANDOM_ITEM_CLUB } }} }}; -static constexpr IndexedArray armorShopTopRowVariationStandart = {{ - {HOUSE_ARMOR_SHOP_EMERALD_ISLAND, { ITEM_TREASURE_LEVEL_1, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_HARMONDALE, { ITEM_TREASURE_LEVEL_1, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_ERATHIA, { ITEM_TREASURE_LEVEL_2, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_TULAREAN_FOREST, { ITEM_TREASURE_LEVEL_2, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_CELESTE, { ITEM_TREASURE_LEVEL_4, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_PIT, { ITEM_TREASURE_LEVEL_4, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_MOUNT_NIGHON, { ITEM_TREASURE_LEVEL_3, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_TATALIA_1, { ITEM_TREASURE_LEVEL_2, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_AVLEE, { ITEM_TREASURE_LEVEL_3, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_STONE_CITY, { ITEM_TREASURE_LEVEL_3, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_CASTLE_HARMONDALE, { ITEM_TREASURE_LEVEL_3, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_TATALIA_2, { ITEM_TREASURE_LEVEL_3, { 35, 35, 38, 38 } }}, - {HOUSE_27, { ITEM_TREASURE_LEVEL_3, { 35, 35, 38, 38 } }}, - {HOUSE_28, { ITEM_TREASURE_LEVEL_4, { 35, 35, 38, 38 } }} +static constexpr IndexedArray armorShopTopRowVariationStandard = {{ + {HOUSE_ARMOR_SHOP_EMERALD_ISLAND, { ITEM_TREASURE_LEVEL_1, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_HARMONDALE, { ITEM_TREASURE_LEVEL_1, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_ERATHIA, { ITEM_TREASURE_LEVEL_2, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_TULAREAN_FOREST, { ITEM_TREASURE_LEVEL_2, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_CELESTE, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_PIT, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_MOUNT_NIGHON, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_TATALIA_1, { ITEM_TREASURE_LEVEL_2, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_AVLEE, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_STONE_CITY, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_CASTLE_HARMONDALE, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_TATALIA_2, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_27, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_28, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }} }}; -static constexpr IndexedArray armorShopBottomRowVariationStandart = {{ - {HOUSE_ARMOR_SHOP_EMERALD_ISLAND, { ITEM_TREASURE_LEVEL_1, { 31, 31, 31, 34 } }}, - {HOUSE_ARMOR_SHOP_HARMONDALE, { ITEM_TREASURE_LEVEL_1, { 31, 31, 32, 34 } }}, - {HOUSE_ARMOR_SHOP_ERATHIA, { ITEM_TREASURE_LEVEL_2, { 31, 32, 32, 33 } }}, - {HOUSE_ARMOR_SHOP_TULAREAN_FOREST, { ITEM_TREASURE_LEVEL_2, { 31, 31, 32, 32 } }}, - {HOUSE_ARMOR_SHOP_CELESTE, { ITEM_TREASURE_LEVEL_4, { 31, 32, 33, 34 } }}, - {HOUSE_ARMOR_SHOP_PIT, { ITEM_TREASURE_LEVEL_4, { 31, 32, 33, 34 } }}, - {HOUSE_ARMOR_SHOP_MOUNT_NIGHON, { ITEM_TREASURE_LEVEL_3, { 31, 31, 31, 31 } }}, - {HOUSE_ARMOR_SHOP_TATALIA_1, { ITEM_TREASURE_LEVEL_2, { 31, 32, 34, 34 } }}, - {HOUSE_ARMOR_SHOP_AVLEE, { ITEM_TREASURE_LEVEL_3, { 31, 31, 32, 32 } }}, - {HOUSE_ARMOR_SHOP_STONE_CITY, { ITEM_TREASURE_LEVEL_3, { 32, 32, 32, 33 } }}, - {HOUSE_ARMOR_SHOP_CASTLE_HARMONDALE, { ITEM_TREASURE_LEVEL_3, { 31, 31, 31, 32 } }}, - {HOUSE_ARMOR_SHOP_TATALIA_2, { ITEM_TREASURE_LEVEL_3, { 33, 31, 32, 34 } }}, - {HOUSE_27, { ITEM_TREASURE_LEVEL_3, { 33, 31, 32, 34 } }}, - {HOUSE_28, { ITEM_TREASURE_LEVEL_4, { 33, 31, 32, 34 } }} +static constexpr IndexedArray armorShopBottomRowVariationStandard = {{ + {HOUSE_ARMOR_SHOP_EMERALD_ISLAND, { ITEM_TREASURE_LEVEL_1, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_SHIELD } }}, + {HOUSE_ARMOR_SHOP_HARMONDALE, { ITEM_TREASURE_LEVEL_1, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_SHIELD } }}, + {HOUSE_ARMOR_SHOP_ERATHIA, { ITEM_TREASURE_LEVEL_2, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_PLATE_ARMOR } }}, + {HOUSE_ARMOR_SHOP_TULAREAN_FOREST, { ITEM_TREASURE_LEVEL_2, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR } }}, + {HOUSE_ARMOR_SHOP_CELESTE, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_PLATE_ARMOR, RANDOM_ITEM_SHIELD } }}, + {HOUSE_ARMOR_SHOP_PIT, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_PLATE_ARMOR, RANDOM_ITEM_SHIELD } }}, + {HOUSE_ARMOR_SHOP_MOUNT_NIGHON, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR } }}, + {HOUSE_ARMOR_SHOP_TATALIA_1, { ITEM_TREASURE_LEVEL_2, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_SHIELD, RANDOM_ITEM_SHIELD } }}, + {HOUSE_ARMOR_SHOP_AVLEE, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR } }}, + {HOUSE_ARMOR_SHOP_STONE_CITY, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_PLATE_ARMOR } }}, + {HOUSE_ARMOR_SHOP_CASTLE_HARMONDALE, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR } }}, + {HOUSE_ARMOR_SHOP_TATALIA_2, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_PLATE_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_SHIELD } }}, + {HOUSE_27, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_PLATE_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_SHIELD } }}, + {HOUSE_28, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_PLATE_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_SHIELD } }} }}; static constexpr IndexedArray armorShopTopRowVariationSpecial = {{ - {HOUSE_ARMOR_SHOP_EMERALD_ISLAND, { ITEM_TREASURE_LEVEL_2, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_HARMONDALE, { ITEM_TREASURE_LEVEL_2, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_ERATHIA, { ITEM_TREASURE_LEVEL_3, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_TULAREAN_FOREST, { ITEM_TREASURE_LEVEL_3, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_CELESTE, { ITEM_TREASURE_LEVEL_5, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_PIT, { ITEM_TREASURE_LEVEL_5, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_MOUNT_NIGHON, { ITEM_TREASURE_LEVEL_4, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_TATALIA_1, { ITEM_TREASURE_LEVEL_3, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_AVLEE, { ITEM_TREASURE_LEVEL_4, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_STONE_CITY, { ITEM_TREASURE_LEVEL_4, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_CASTLE_HARMONDALE, { ITEM_TREASURE_LEVEL_4, { 35, 35, 38, 38 } }}, - {HOUSE_ARMOR_SHOP_TATALIA_2, { ITEM_TREASURE_LEVEL_4, { 35, 35, 38, 38 } }}, - {HOUSE_27, { ITEM_TREASURE_LEVEL_4, { 35, 35, 38, 38 } }}, - {HOUSE_28, { ITEM_TREASURE_LEVEL_5, { 35, 35, 38, 38 } }} + {HOUSE_ARMOR_SHOP_EMERALD_ISLAND, { ITEM_TREASURE_LEVEL_2, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_HARMONDALE, { ITEM_TREASURE_LEVEL_2, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_ERATHIA, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_TULAREAN_FOREST, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_CELESTE, { ITEM_TREASURE_LEVEL_5, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_PIT, { ITEM_TREASURE_LEVEL_5, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_MOUNT_NIGHON, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_TATALIA_1, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_AVLEE, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_STONE_CITY, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_CASTLE_HARMONDALE, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_ARMOR_SHOP_TATALIA_2, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_27, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }}, + {HOUSE_28, { ITEM_TREASURE_LEVEL_5, { RANDOM_ITEM_HELMET, RANDOM_ITEM_HELMET, RANDOM_ITEM_GAUNTLETS, RANDOM_ITEM_GAUNTLETS } }} }}; static constexpr IndexedArray armorShopBottomRowVariationSpecial = {{ - {HOUSE_ARMOR_SHOP_EMERALD_ISLAND, { ITEM_TREASURE_LEVEL_2, { 31, 31, 31, 34 } }}, - {HOUSE_ARMOR_SHOP_HARMONDALE, { ITEM_TREASURE_LEVEL_2, { 31, 31, 32, 34 } }}, - {HOUSE_ARMOR_SHOP_ERATHIA, { ITEM_TREASURE_LEVEL_3, { 31, 32, 32, 33 } }}, - {HOUSE_ARMOR_SHOP_TULAREAN_FOREST, { ITEM_TREASURE_LEVEL_3, { 31, 31, 32, 32 } }}, - {HOUSE_ARMOR_SHOP_CELESTE, { ITEM_TREASURE_LEVEL_5, { 31, 32, 33, 34 } }}, - {HOUSE_ARMOR_SHOP_PIT, { ITEM_TREASURE_LEVEL_5, { 31, 32, 33, 34 } }}, - {HOUSE_ARMOR_SHOP_MOUNT_NIGHON, { ITEM_TREASURE_LEVEL_4, { 31, 31, 31, 31 } }}, - {HOUSE_ARMOR_SHOP_TATALIA_1, { ITEM_TREASURE_LEVEL_3, { 31, 32, 34, 34 } }}, - {HOUSE_ARMOR_SHOP_AVLEE, { ITEM_TREASURE_LEVEL_4, { 31, 31, 32, 33 } }}, - {HOUSE_ARMOR_SHOP_STONE_CITY, { ITEM_TREASURE_LEVEL_4, { 32, 32, 33, 34 } }}, - {HOUSE_ARMOR_SHOP_CASTLE_HARMONDALE, { ITEM_TREASURE_LEVEL_4, { 31, 31, 31, 32 } }}, - {HOUSE_ARMOR_SHOP_TATALIA_2, { ITEM_TREASURE_LEVEL_4, { 32, 32, 32, 32 } }}, - {HOUSE_27, { ITEM_TREASURE_LEVEL_4, { 34, 34, 34, 34 } }}, - {HOUSE_28, { ITEM_TREASURE_LEVEL_5, { 33, 33, 33, 33 } }} + {HOUSE_ARMOR_SHOP_EMERALD_ISLAND, { ITEM_TREASURE_LEVEL_2, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_SHIELD } }}, + {HOUSE_ARMOR_SHOP_HARMONDALE, { ITEM_TREASURE_LEVEL_2, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_SHIELD } }}, + {HOUSE_ARMOR_SHOP_ERATHIA, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_PLATE_ARMOR } }}, + {HOUSE_ARMOR_SHOP_TULAREAN_FOREST, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR } }}, + {HOUSE_ARMOR_SHOP_CELESTE, { ITEM_TREASURE_LEVEL_5, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_PLATE_ARMOR, RANDOM_ITEM_SHIELD } }}, + {HOUSE_ARMOR_SHOP_PIT, { ITEM_TREASURE_LEVEL_5, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_PLATE_ARMOR, RANDOM_ITEM_SHIELD } }}, + {HOUSE_ARMOR_SHOP_MOUNT_NIGHON, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR } }}, + {HOUSE_ARMOR_SHOP_TATALIA_1, { ITEM_TREASURE_LEVEL_3, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_SHIELD, RANDOM_ITEM_SHIELD } }}, + {HOUSE_ARMOR_SHOP_AVLEE, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_PLATE_ARMOR } }}, + {HOUSE_ARMOR_SHOP_STONE_CITY, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_PLATE_ARMOR, RANDOM_ITEM_SHIELD } }}, + {HOUSE_ARMOR_SHOP_CASTLE_HARMONDALE, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR } }}, + {HOUSE_ARMOR_SHOP_TATALIA_2, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR, RANDOM_ITEM_LEATHER_ARMOR } }}, + {HOUSE_27, { ITEM_TREASURE_LEVEL_4, { RANDOM_ITEM_SHIELD, RANDOM_ITEM_SHIELD, RANDOM_ITEM_SHIELD, RANDOM_ITEM_SHIELD } }}, + {HOUSE_28, { ITEM_TREASURE_LEVEL_5, { RANDOM_ITEM_PLATE_ARMOR, RANDOM_ITEM_PLATE_ARMOR, RANDOM_ITEM_PLATE_ARMOR, RANDOM_ITEM_PLATE_ARMOR } }} }}; -static constexpr IndexedArray magicShopVariationStandart = {{ +static constexpr IndexedArray magicShopVariationStandard = {{ {HOUSE_MAGIC_SHOP_EMERALD_ISLAND, ITEM_TREASURE_LEVEL_1}, {HOUSE_MAGIC_SHOP_HARMONDALE, ITEM_TREASURE_LEVEL_1}, {HOUSE_MAGIC_SHOP_ERATHIA, ITEM_TREASURE_LEVEL_2}, @@ -175,7 +173,7 @@ static constexpr IndexedArray alchemyShopVariationStandart = {{ +static constexpr IndexedArray alchemyShopVariationStandard = {{ {HOUSE_ALCHEMY_SHOP_EMERALD_ISLAND, ITEM_TREASURE_LEVEL_1}, {HOUSE_ALCHEMY_SHOP_HARMONDALE, ITEM_TREASURE_LEVEL_1}, {HOUSE_ALCHEMY_SHOP_ERATHIA, ITEM_TREASURE_LEVEL_2}, @@ -214,37 +212,37 @@ bool isStealingModeActive() { return keyboardInputHandler->IsStealingToggled() && pParty->activeCharacter().CanSteal(); } -void addUniqueItemClasses(const ITEM_VARIATION &variation, std::vector &set) { - for (int itemClass : variation.item_class) { +void addUniqueItemClasses(const ITEM_VARIATION &variation, std::vector &set) { + for (RandomItemType itemClass : variation.item_class) { if (std::find(set.begin(), set.end(), itemClass) == set.end()) { set.push_back(itemClass); } } } -DIALOGUE_TYPE getSkillLearnDualogueForItemClass(int itemClass) { +DIALOGUE_TYPE getSkillLearnDualogueForItemClass(RandomItemType itemClass) { switch (itemClass) { - case 23: + case RANDOM_ITEM_SWORD: return DIALOGUE_LEARN_SWORD; - case 24: + case RANDOM_ITEM_DAGGER: return DIALOGUE_LEARN_DAGGER; - case 25: + case RANDOM_ITEM_AXE: return DIALOGUE_LEARN_AXE; - case 26: + case RANDOM_ITEM_SPEAR: return DIALOGUE_LEARN_SPEAR; - case 27: + case RANDOM_ITEM_BOW: return DIALOGUE_LEARN_BOW; - case 28: + case RANDOM_ITEM_MACE: return DIALOGUE_LEARN_MACE; - case 30: + case RANDOM_ITEM_STAFF: return DIALOGUE_LEARN_STAFF; - case 31: + case RANDOM_ITEM_LEATHER_ARMOR: return DIALOGUE_LEARN_LEATHER; - case 32: + case RANDOM_ITEM_CHAIN_ARMOR: return DIALOGUE_LEARN_CHAIN; - case 33: + case RANDOM_ITEM_PLATE_ARMOR: return DIALOGUE_LEARN_PLATE; - case 34: + case RANDOM_ITEM_SHIELD: return DIALOGUE_LEARN_SHIELD; default: return DIALOGUE_NULL; @@ -297,8 +295,8 @@ void GUIWindow_Shop::sellDialogue() { int pItemID = pParty->activeCharacter().GetItemListAtInventoryIndex(invindex); if (pItemID) { ItemGen *item = &pParty->activeCharacter().pInventoryItemList[pItemID - 1]; - MerchantPhrase phrases_id = pParty->activeCharacter().SelectPhrasesTransaction(item, buildingType(), houseId(), 3); - std::string str = BuildDialogueString(pMerchantsSellPhrases[phrases_id], pParty->activeCharacterIndex() - 1, item, houseId(), 3); + MerchantPhrase phrases_id = pParty->activeCharacter().SelectPhrasesTransaction(item, buildingType(), houseId(), SHOP_SCREEN_SELL); + std::string str = BuildDialogueString(pMerchantsSellPhrases[phrases_id], pParty->activeCharacterIndex() - 1, item, houseId(), SHOP_SCREEN_SELL); int vertMargin = (SIDE_TEXT_BOX_BODY_TEXT_HEIGHT - assets->pFontArrus->CalcTextHeight(str, dialogwin.uFrameWidth, 0)) / 2 + SIDE_TEXT_BOX_BODY_TEXT_OFFSET; dialogwin.DrawTitleText(assets->pFontArrus.get(), 0, vertMargin, colorTable.White, str, 3); } @@ -331,10 +329,10 @@ void GUIWindow_Shop::identifyDialogue() { std::string str; if (!item->IsIdentified()) { - MerchantPhrase phrases_id = pParty->activeCharacter().SelectPhrasesTransaction(item, buildingType(), houseId(), 4); - str = BuildDialogueString(pMerchantsIdentifyPhrases[phrases_id], pParty->activeCharacterIndex() - 1, item, houseId(), 4); + MerchantPhrase phrases_id = pParty->activeCharacter().SelectPhrasesTransaction(item, buildingType(), houseId(), SHOP_SCREEN_IDENTIFY); + str = BuildDialogueString(pMerchantsIdentifyPhrases[phrases_id], pParty->activeCharacterIndex() - 1, item, houseId(), SHOP_SCREEN_IDENTIFY); } else { - str = BuildDialogueString("%24", pParty->activeCharacterIndex() - 1, item, houseId(), 4); + str = BuildDialogueString("%24", pParty->activeCharacterIndex() - 1, item, houseId(), SHOP_SCREEN_IDENTIFY); } int vertMargin = (SIDE_TEXT_BOX_BODY_TEXT_HEIGHT - assets->pFontArrus->CalcTextHeight(str, dialogwin.uFrameWidth, 0)) / 2 + SIDE_TEXT_BOX_BODY_TEXT_OFFSET; @@ -368,8 +366,8 @@ void GUIWindow_Shop::repairDialogue() { if (pParty->activeCharacter().pOwnItems[pItemID - 1].uAttributes & ITEM_BROKEN) { ItemGen *item = &pParty->activeCharacter().pInventoryItemList[pItemID - 1]; - MerchantPhrase phrases_id = pParty->activeCharacter().SelectPhrasesTransaction(item, buildingType(), houseId(), 5); - std::string str = BuildDialogueString(pMerchantsRepairPhrases[phrases_id], pParty->activeCharacterIndex() - 1, item, houseId(), 5); + MerchantPhrase phrases_id = pParty->activeCharacter().SelectPhrasesTransaction(item, buildingType(), houseId(), SHOP_SCREEN_REPAIR); + std::string str = BuildDialogueString(pMerchantsRepairPhrases[phrases_id], pParty->activeCharacterIndex() - 1, item, houseId(), SHOP_SCREEN_REPAIR); int vertMargin = (SIDE_TEXT_BOX_BODY_TEXT_HEIGHT - assets->pFontArrus->CalcTextHeight(str, dialogwin.uFrameWidth, 0)) / 2 + SIDE_TEXT_BOX_BODY_TEXT_OFFSET; dialogwin.DrawTitleText(assets->pFontArrus.get(), 0, vertMargin, colorTable.White, str, 3); } @@ -426,10 +424,10 @@ void GUIWindow_WeaponShop::shopWaresDialogue(bool isSpecial) { if (pt.y >= weaponYPos[testx] + 30 && pt.y < (weaponYPos[testx] + 30 + (shop_ui_items_in_store[testx]->height()))) { std::string str; if (!isStealingModeActive()) { - MerchantPhrase phrase = pParty->activeCharacter().SelectPhrasesTransaction(item, BUILDING_WEAPON_SHOP, houseId(), 2); - str = BuildDialogueString(pMerchantsBuyPhrases[phrase], pParty->activeCharacterIndex() - 1, item, houseId(), 2); + MerchantPhrase phrase = pParty->activeCharacter().SelectPhrasesTransaction(item, BUILDING_WEAPON_SHOP, houseId(), SHOP_SCREEN_BUY); + str = BuildDialogueString(pMerchantsBuyPhrases[phrase], pParty->activeCharacterIndex() - 1, item, houseId(), SHOP_SCREEN_BUY); } else { - str = BuildDialogueString(localization->GetString(LSTR_STEAL_ITEM_FMT), pParty->activeCharacterIndex() - 1, item, houseId(), 2); + str = BuildDialogueString(localization->GetString(LSTR_STEAL_ITEM_FMT), pParty->activeCharacterIndex() - 1, item, houseId(), SHOP_SCREEN_BUY); } int vertMargin = (SIDE_TEXT_BOX_BODY_TEXT_HEIGHT - assets->pFontArrus->CalcTextHeight(str, dialogwin.uFrameWidth, 0)) / 2 + SIDE_TEXT_BOX_BODY_TEXT_OFFSET; dialogwin.DrawTitleText(assets->pFontArrus.get(), 0, vertMargin, colorTable.White, str, 3); @@ -513,10 +511,10 @@ void GUIWindow_ArmorShop::shopWaresDialogue(bool isSpecial) { std::string str; if (!isStealingModeActive()) { - MerchantPhrase phrase = pParty->activeCharacter().SelectPhrasesTransaction(item, buildingType(), houseId(), 2); - str = BuildDialogueString(pMerchantsBuyPhrases[phrase], pParty->activeCharacterIndex() - 1, item, houseId(), 2); + MerchantPhrase phrase = pParty->activeCharacter().SelectPhrasesTransaction(item, buildingType(), houseId(), SHOP_SCREEN_BUY); + str = BuildDialogueString(pMerchantsBuyPhrases[phrase], pParty->activeCharacterIndex() - 1, item, houseId(), SHOP_SCREEN_BUY); } else { - str = BuildDialogueString(localization->GetString(LSTR_STEAL_ITEM_FMT), pParty->activeCharacterIndex() - 1, item, houseId(), 2); + str = BuildDialogueString(localization->GetString(LSTR_STEAL_ITEM_FMT), pParty->activeCharacterIndex() - 1, item, houseId(), SHOP_SCREEN_BUY); } int vertMargin = (SIDE_TEXT_BOX_BODY_TEXT_HEIGHT - assets->pFontArrus->CalcTextHeight(str, dialogwin.uFrameWidth, 0)) / 2 + SIDE_TEXT_BOX_BODY_TEXT_OFFSET; dialogwin.DrawTitleText(assets->pFontArrus.get(), 0, vertMargin, colorTable.White, str, 3); @@ -617,10 +615,10 @@ void GUIWindow_MagicAlchemyShop::shopWaresDialogue(bool isSpecial) { std::string str; if (!isStealingModeActive()) { - MerchantPhrase phrase = pParty->activeCharacter().SelectPhrasesTransaction(item, buildingType(), houseId(), 2); - str = BuildDialogueString(pMerchantsBuyPhrases[phrase], pParty->activeCharacterIndex() - 1, item, houseId(), 2); + MerchantPhrase phrase = pParty->activeCharacter().SelectPhrasesTransaction(item, buildingType(), houseId(), SHOP_SCREEN_BUY); + str = BuildDialogueString(pMerchantsBuyPhrases[phrase], pParty->activeCharacterIndex() - 1, item, houseId(), SHOP_SCREEN_BUY); } else { - str = BuildDialogueString(localization->GetString(LSTR_STEAL_ITEM_FMT), pParty->activeCharacterIndex() - 1, item, houseId(), 2); + str = BuildDialogueString(localization->GetString(LSTR_STEAL_ITEM_FMT), pParty->activeCharacterIndex() - 1, item, houseId(), SHOP_SCREEN_BUY); } int vertMargin = (SIDE_TEXT_BOX_BODY_TEXT_HEIGHT - assets->pFontArrus->CalcTextHeight(str, dialogwin.uFrameWidth, 0)) / 2 + SIDE_TEXT_BOX_BODY_TEXT_OFFSET; dialogwin.DrawTitleText(assets->pFontArrus.get(), 0, vertMargin, colorTable.White, str, 3); @@ -636,10 +634,10 @@ void GUIWindow_MagicAlchemyShop::shopWaresDialogue(bool isSpecial) { void GUIWindow_WeaponShop::generateShopItems(bool isSpecial) { std::array &itemArray = isSpecial ? pParty->specialItemsInShops[houseId()] : pParty->standartItemsInShops[houseId()]; - const ITEM_VARIATION variation = isSpecial ? weaponShopVariationSpecial[houseId()] : weaponShopVariationStandart[houseId()]; + const ITEM_VARIATION variation = isSpecial ? weaponShopVariationSpecial[houseId()] : weaponShopVariationStandard[houseId()]; for (int i = 0; i < itemAmountInShop[buildingType()]; i++) { - int itemClass = variation.item_class[grng->random(4)]; + RandomItemType itemClass = variation.item_class[grng->random(4)]; pItemTable->generateItem(variation.treasure_level, itemClass, &itemArray[i]); itemArray[i].SetIdentified(); } @@ -649,11 +647,11 @@ void GUIWindow_WeaponShop::generateShopItems(bool isSpecial) { void GUIWindow_ArmorShop::generateShopItems(bool isSpecial) { std::array &itemArray = isSpecial ? pParty->specialItemsInShops[houseId()] : pParty->standartItemsInShops[houseId()]; - const ITEM_VARIATION variationTop = isSpecial ? armorShopTopRowVariationSpecial[houseId()] : armorShopTopRowVariationStandart[houseId()]; - const ITEM_VARIATION variationBottom = isSpecial ? armorShopBottomRowVariationSpecial[houseId()] : armorShopBottomRowVariationStandart[houseId()]; + const ITEM_VARIATION variationTop = isSpecial ? armorShopTopRowVariationSpecial[houseId()] : armorShopTopRowVariationStandard[houseId()]; + const ITEM_VARIATION variationBottom = isSpecial ? armorShopBottomRowVariationSpecial[houseId()] : armorShopBottomRowVariationStandard[houseId()]; for (int i = 0; i < itemAmountInShop[buildingType()]; i++) { - int itemClass; + RandomItemType itemClass; ItemTreasureLevel treasureLvl; if (i >= 4) { @@ -672,10 +670,10 @@ void GUIWindow_ArmorShop::generateShopItems(bool isSpecial) { void GUIWindow_MagicShop::generateShopItems(bool isSpecial) { std::array &itemArray = isSpecial ? pParty->specialItemsInShops[houseId()] : pParty->standartItemsInShops[houseId()]; - ItemTreasureLevel treasureLvl = isSpecial ? magicShopVariationSpecial[houseId()] : magicShopVariationStandart[houseId()]; + ItemTreasureLevel treasureLvl = isSpecial ? magicShopVariationSpecial[houseId()] : magicShopVariationStandard[houseId()]; for (int i = 0; i < itemAmountInShop[buildingType()]; i++) { - pItemTable->generateItem(treasureLvl, 22, &itemArray[i]); + pItemTable->generateItem(treasureLvl, RANDOM_ITEM_MICS, &itemArray[i]); itemArray[i].SetIdentified(); } @@ -684,8 +682,8 @@ void GUIWindow_MagicShop::generateShopItems(bool isSpecial) { void GUIWindow_AlchemyShop::generateShopItems(bool isSpecial) { std::array &itemArray = isSpecial ? pParty->specialItemsInShops[houseId()] : pParty->standartItemsInShops[houseId()]; - ItemTreasureLevel treasureLvl = isSpecial ? alchemyShopVariationSpecial[houseId()] : alchemyShopVariationStandart[houseId()]; - int bottomRowItemClass = isSpecial ? 44 : 45; + ItemTreasureLevel treasureLvl = isSpecial ? alchemyShopVariationSpecial[houseId()] : alchemyShopVariationStandard[houseId()]; + RandomItemType bottomRowItemClass = isSpecial ? RANDOM_ITEM_POTION : RANDOM_ITEM_REAGENT; for (int i = 0; i < itemAmountInShop[buildingType()]; i++) { if (i < 6) { @@ -705,13 +703,13 @@ void GUIWindow_AlchemyShop::generateShopItems(bool isSpecial) { } std::vector GUIWindow_WeaponShop::listShopLearnableSkills() { - std::vector itemClasses; + std::vector itemClasses; std::vector skillsOptions; - addUniqueItemClasses(weaponShopVariationStandart[houseId()], itemClasses); + addUniqueItemClasses(weaponShopVariationStandard[houseId()], itemClasses); addUniqueItemClasses(weaponShopVariationSpecial[houseId()], itemClasses); - for (int itemClass : itemClasses) { + for (RandomItemType itemClass : itemClasses) { DIALOGUE_TYPE dialogue = getSkillLearnDualogueForItemClass(itemClass); if (dialogue != DIALOGUE_NULL) { skillsOptions.push_back(dialogue); @@ -722,15 +720,15 @@ std::vector GUIWindow_WeaponShop::listShopLearnableSkills() { } std::vector GUIWindow_ArmorShop::listShopLearnableSkills() { - std::vector itemClasses; + std::vector itemClasses; std::vector skillsOptions; - addUniqueItemClasses(armorShopTopRowVariationStandart[houseId()], itemClasses); - addUniqueItemClasses(armorShopBottomRowVariationStandart[houseId()], itemClasses); + addUniqueItemClasses(armorShopTopRowVariationStandard[houseId()], itemClasses); + addUniqueItemClasses(armorShopBottomRowVariationStandard[houseId()], itemClasses); addUniqueItemClasses(armorShopTopRowVariationSpecial[houseId()], itemClasses); addUniqueItemClasses(armorShopBottomRowVariationSpecial[houseId()], itemClasses); - for (int itemClass : itemClasses) { + for (RandomItemType itemClass : itemClasses) { DIALOGUE_TYPE dialogue = getSkillLearnDualogueForItemClass(itemClass); if (dialogue != DIALOGUE_NULL) { skillsOptions.push_back(dialogue); @@ -917,7 +915,7 @@ void GUIWindow_Shop::houseScreenClick() { return; } - if (pParty->activeCharacter().pInventoryItemList[pItemID - 1].MerchandiseTest(houseId())) { + if (pParty->activeCharacter().pInventoryItemList[pItemID - 1].canSellRepairIdentifyAt(houseId())) { _transactionPerformed = true; pParty->activeCharacter().SalesProcess(invindex, pItemID - 1, houseId()); render->ClearZBuffer(); @@ -946,7 +944,7 @@ void GUIWindow_Shop::houseScreenClick() { ItemGen &item = pParty->activeCharacter().pInventoryItemList[pItemID - 1]; if (!(item.uAttributes & ITEM_IDENTIFIED)) { - if (item.MerchandiseTest(houseId())) { + if (item.canSellRepairIdentifyAt(houseId())) { if (pParty->GetGold() >= uPriceItemService) { _transactionPerformed = true; pParty->TakeGold(uPriceItemService); @@ -986,7 +984,7 @@ void GUIWindow_Shop::houseScreenClick() { int uPriceItemService = PriceCalculator::itemRepairPriceForPlayer(&pParty->activeCharacter(), item.GetValue(), fPriceMultiplier); if (item.uAttributes & ITEM_BROKEN) { - if (item.MerchandiseTest(houseId())) { + if (item.canSellRepairIdentifyAt(houseId())) { if (pParty->GetGold() >= uPriceItemService) { _transactionPerformed = true; pParty->TakeGold(uPriceItemService); diff --git a/src/GUI/UI/Houses/TownHall.cpp b/src/GUI/UI/Houses/TownHall.cpp index 4e14cdf28915..f5418d9bf256 100644 --- a/src/GUI/UI/Houses/TownHall.cpp +++ b/src/GUI/UI/Houses/TownHall.cpp @@ -144,8 +144,9 @@ std::vector GUIWindow_TownHall::listDialogueOptions() { } } -int GUIWindow_TownHall::randomMonsterForHunting(HOUSE_ID townhall) { +MONSTER_TYPE GUIWindow_TownHall::randomMonsterForHunting(HOUSE_ID townhall) { while (true) { + // TODO(captainurist): I got lazy here. Use actual enum values. int result = grng->random(258) + 1; switch (townhall) { case HOUSE_TOWN_HALL_HARMONDALE: @@ -161,7 +162,7 @@ int GUIWindow_TownHall::randomMonsterForHunting(HOUSE_ID townhall) { (result < 0xFDu || result > 0xFFu) && (result < 0x6Du || result > 0x6Fu) && (result < 0x61u || result > 0x63u)) - return result; + return static_cast(result); break; case HOUSE_TOWN_HALL_ERATHIA: @@ -180,7 +181,7 @@ int GUIWindow_TownHall::randomMonsterForHunting(HOUSE_ID townhall) { (result < 0xFDu || result > 0xFFu) && (result < 0x61u || result > 0x63u) && (result < 0xCDu || result > 0xCFu)) - return result; + return static_cast(result); break; case HOUSE_TOWN_HALL_TULAREAN_FOREST: @@ -196,7 +197,7 @@ int GUIWindow_TownHall::randomMonsterForHunting(HOUSE_ID townhall) { (result < 0xFDu || result > 0xFFu) && (result < 0x61u || result > 0x63u) && (result < 0x1Cu || result > 0x1Eu)) - return result; + return static_cast(result); break; case HOUSE_TOWN_HALL_CELESTE: @@ -215,7 +216,7 @@ int GUIWindow_TownHall::randomMonsterForHunting(HOUSE_ID townhall) { (result < 0xFDu || result > 0xFFu) && (result < 0x61u || result > 0x63u) && (result < 0x6Au || result > 0x6Cu)) - return result; + return static_cast(result); break; case HOUSE_TOWN_HALL_PIT: @@ -238,12 +239,12 @@ int GUIWindow_TownHall::randomMonsterForHunting(HOUSE_ID townhall) { (result < 0xFDu || result > 0xFFu) && (result < 0x61u || result > 0x63u) && (result < 0x10u || result > 0x12u)) - return result; + return static_cast(result); break; default: assert(false); - return -1; + return MONSTER_0; } } } @@ -261,14 +262,14 @@ void GUIWindow_TownHall::bountyHuntingDialogueOptionClicked() { _bountyHuntMonsterId = pParty->monster_id_for_hunting[house]; if (!pParty->monster_for_hunting_killed[house]) { - if (pParty->monster_id_for_hunting[house]) { + if (pParty->monster_id_for_hunting[house] != MONSTER_0) { _bountyHuntText = pNPCTopics[351].pText; // "This month's bounty is on a %s..." } else { _bountyHuntText = pNPCTopics[353].pText; // "Someone has already claimed the bounty this month..." } } else { // Get prize - if (pParty->monster_id_for_hunting[house]) { + if (pParty->monster_id_for_hunting[house] != MONSTER_0) { int bounty = 100 * pMonsterStats->pInfos[pParty->monster_id_for_hunting[house]].uLevel; pParty->partyFindsGold(bounty, GOLD_RECEIVE_SHARE); @@ -276,7 +277,7 @@ void GUIWindow_TownHall::bountyHuntingDialogueOptionClicked() { player.SetVariable(VAR_Award, Award_BountiesCollected); } pParty->uNumBountiesCollected += bounty; - pParty->monster_id_for_hunting[house] = 0; + pParty->monster_id_for_hunting[house] = MONSTER_0; pParty->monster_for_hunting_killed[house] = false; } @@ -286,7 +287,7 @@ void GUIWindow_TownHall::bountyHuntingDialogueOptionClicked() { std::string GUIWindow_TownHall::bountyHuntingText() { assert(!_bountyHuntText.empty()); - assert(_bountyHuntMonsterId != 0); + assert(_bountyHuntMonsterId != MONSTER_0); // TODO(captainurist): what do we do with exceptions inside fmt? std::string name = fmt::format("{::}{}{::}", colorTable.PaleCanary.tag(), pMonsterStats->pInfos[_bountyHuntMonsterId].pName, colorTable.White.tag()); diff --git a/src/GUI/UI/Houses/TownHall.h b/src/GUI/UI/Houses/TownHall.h index 40751cb8fbb5..1fb730066d0d 100644 --- a/src/GUI/UI/Houses/TownHall.h +++ b/src/GUI/UI/Houses/TownHall.h @@ -3,6 +3,8 @@ #include #include +#include "Engine/Objects/Monsters.h" // TODO(captainurist): MonsterEnums.h + #include "GUI/UI/UIHouses.h" #include "GUI/UI/UIHouseEnums.h" @@ -26,7 +28,7 @@ class GUIWindow_TownHall : public GUIWindow_House { void payFineDialogue(); private: - int randomMonsterForHunting(HOUSE_ID townhall); + MONSTER_TYPE randomMonsterForHunting(HOUSE_ID townhall); /** * Handler for the "Bounty Hunt" dialogue option in a town hall. @@ -37,5 +39,5 @@ class GUIWindow_TownHall : public GUIWindow_House { void bountyHuntingDialogueOptionClicked(); std::string _bountyHuntText = ""; - int _bountyHuntMonsterId = 0; + MONSTER_TYPE _bountyHuntMonsterId = MONSTER_0; }; diff --git a/src/GUI/UI/NPCTopics.cpp b/src/GUI/UI/NPCTopics.cpp index 1ba2608f6b4c..4ed4eb6ea105 100644 --- a/src/GUI/UI/NPCTopics.cpp +++ b/src/GUI/UI/NPCTopics.cpp @@ -260,9 +260,8 @@ DIALOGUE_TYPE arenaMainDialogue() { * @offset 0x4BC109 */ void prepareArenaFight(DIALOGUE_TYPE dialogue) { - const int LAST_ARENA_FIGHTER_TYPE = 258; - std::vector monsterIds; - std::vector monsterTypes; + std::vector monsterIds; + std::vector monsterTypes; pParty->field_7B5_in_arena_quest = dialogue; GUIWindow window = *pDialogueWindow; @@ -327,7 +326,7 @@ void prepareArenaFight(DIALOGUE_TYPE dialogue) { if (monsterMaxLevel < 2) monsterMaxLevel = 2; - for (int i = 1; i <= LAST_ARENA_FIGHTER_TYPE; i++) { + for (MONSTER_TYPE i : allArenaMonsters()) { if (pMonsterStats->pInfos[i].uAIType != 1) { if (!MonsterStats::BelongsToSupertype(pMonsterStats->pInfos[i].uID, MONSTER_SUPERTYPE_8)) { if (pMonsterStats->pInfos[i].uLevel >= monsterMinLevel && diff --git a/src/GUI/UI/UIDialogue.cpp b/src/GUI/UI/UIDialogue.cpp index 0f4d626aa1e8..cb3bfaa2f6ac 100644 --- a/src/GUI/UI/UIDialogue.cpp +++ b/src/GUI/UI/UIDialogue.cpp @@ -192,16 +192,16 @@ void GUIWindow_Dialogue::Update() { std::string dialogue_string; switch (_displayedDialogue) { case DIALOGUE_13_hiring_related: - dialogue_string = BuildDialogueString(pNPCStats->pProfessions[pNPC->profession].pJoinText, 0, 0, HOUSE_INVALID, 0); + dialogue_string = BuildDialogueString(pNPCStats->pProfessions[pNPC->profession].pJoinText, 0); break; case DIALOGUE_PROFESSION_DETAILS: { if (dialogue_show_profession_details) { - dialogue_string = BuildDialogueString(pNPCStats->pProfessions[pNPC->profession].pBenefits, 0, 0, HOUSE_INVALID, 0); + dialogue_string = BuildDialogueString(pNPCStats->pProfessions[pNPC->profession].pBenefits, 0); } else if (pNPC->Hired()) { - dialogue_string = BuildDialogueString(pNPCStats->pProfessions[pNPC->profession].pDismissText, 0, 0, HOUSE_INVALID, 0); + dialogue_string = BuildDialogueString(pNPCStats->pProfessions[pNPC->profession].pDismissText, 0); } else { - dialogue_string = BuildDialogueString(pNPCStats->pProfessions[pNPC->profession].pJoinText, 0, 0, HOUSE_INVALID, 0); + dialogue_string = BuildDialogueString(pNPCStats->pProfessions[pNPC->profession].pJoinText, 0); } break; } @@ -237,9 +237,9 @@ void GUIWindow_Dialogue::Update() { NPCProfession *prof = &pNPCStats->pProfessions[pNPC->profession]; if (pNPC->Hired()) { - dialogue_string = BuildDialogueString(prof->pDismissText, 0, 0, HOUSE_INVALID, 0); + dialogue_string = BuildDialogueString(prof->pDismissText, 0); } else { - dialogue_string = BuildDialogueString(prof->pJoinText, 0, 0, HOUSE_INVALID, 0); + dialogue_string = BuildDialogueString(prof->pJoinText, 0); } } break; diff --git a/src/GUI/UI/UIHouses.cpp b/src/GUI/UI/UIHouses.cpp index 3f3a52785a64..6078f5e24f39 100644 --- a/src/GUI/UI/UIHouses.cpp +++ b/src/GUI/UI/UIHouses.cpp @@ -490,7 +490,7 @@ void selectHouseNPCDialogueOption(DIALOGUE_TYPE topic) { if (topic == DIALOGUE_13_hiring_related) { current_npc_text = BuildDialogueString(pNPCStats->pProfessions[pCurrentNPCInfo->profession].pJoinText, - pParty->activeCharacterIndex() - 1, 0, HOUSE_INVALID, 0); + pParty->activeCharacterIndex() - 1); NPCHireableDialogPrepare(); dialogue_show_profession_details = false; BackToHouseMenu(); @@ -503,10 +503,10 @@ void selectHouseNPCDialogueOption(DIALOGUE_TYPE topic) { if (topic == DIALOGUE_PROFESSION_DETAILS) { if (dialogue_show_profession_details) { current_npc_text = BuildDialogueString(pNPCStats->pProfessions[pCurrentNPCInfo->profession].pBenefits, - pParty->activeCharacterIndex() - 1, 0, HOUSE_INVALID, 0); + pParty->activeCharacterIndex() - 1); } else { current_npc_text = BuildDialogueString(pNPCStats->pProfessions[pCurrentNPCInfo->profession].pJoinText, - pParty->activeCharacterIndex() - 1, 0, HOUSE_INVALID, 0); + pParty->activeCharacterIndex() - 1); } } BackToHouseMenu(); @@ -515,7 +515,7 @@ void selectHouseNPCDialogueOption(DIALOGUE_TYPE topic) { if (!pCurrentNPCInfo->Hired()) { current_npc_text = BuildDialogueString(pNPCStats->pProfessions[pCurrentNPCInfo->profession].pJoinText, - pParty->activeCharacterIndex() - 1, 0, HOUSE_INVALID, 0); + pParty->activeCharacterIndex() - 1); BackToHouseMenu(); return; } diff --git a/src/GUI/UI/UIPartyCreation.cpp b/src/GUI/UI/UIPartyCreation.cpp index fa88486dfc62..7dd1e501a685 100644 --- a/src/GUI/UI/UIPartyCreation.cpp +++ b/src/GUI/UI/UIPartyCreation.cpp @@ -773,7 +773,7 @@ bool PartyCreationUI_LoopInternal() { break; } } - pItemTable->generateItem(ITEM_TREASURE_LEVEL_2, 40, &item); + pItemTable->generateItem(ITEM_TREASURE_LEVEL_2, RANDOM_ITEM_RING, &item); pParty->pCharacters[i].AddItem2(-1, &item); pParty->pCharacters[i].health = pParty->pCharacters[i].GetMaxHealth(); diff --git a/src/GUI/UI/UIPopup.cpp b/src/GUI/UI/UIPopup.cpp index f602a8537ae0..22a90b2cb5f1 100644 --- a/src/GUI/UI/UIPopup.cpp +++ b/src/GUI/UI/UIPopup.cpp @@ -554,7 +554,8 @@ void GameUI_DrawItemInfo(struct ItemGen *inspect_item) { void MonsterPopup_Draw(unsigned int uActorID, GUIWindow *pWindow) { static Actor pMonsterInfoUI_Doll; - int Popup_Y_Offset = monster_popup_y_offsets[(pActors[uActorID].monsterInfo.uID - 1) / 3] - 40; + // TODO(captainurist): encapsulate enum arithmetic. + int Popup_Y_Offset = monster_popup_y_offsets[(std::to_underlying(pActors[uActorID].monsterInfo.uID) - 1) / 3] - 40; uint16_t actionLen = 0; if (pActors[uActorID].monsterInfo.uID == pMonsterInfoUI_Doll.monsterInfo.uID) { @@ -577,10 +578,10 @@ void MonsterPopup_Draw(unsigned int uActorID, GUIWindow *pWindow) { } else { // rand(); pMonsterInfoUI_Doll.currentActionAnimation = ANIM_Bored; - if ((pMonsterInfoUI_Doll.monsterInfo.uID < 115 || - pMonsterInfoUI_Doll.monsterInfo.uID > 186) && - (pMonsterInfoUI_Doll.monsterInfo.uID < 232 || - pMonsterInfoUI_Doll.monsterInfo.uID > 249) && vrng->random(30) < 100) + if ((pMonsterInfoUI_Doll.monsterInfo.uID < MONSTER_115 || + pMonsterInfoUI_Doll.monsterInfo.uID > MONSTER_186) && + (pMonsterInfoUI_Doll.monsterInfo.uID < MONSTER_232 || + pMonsterInfoUI_Doll.monsterInfo.uID > MONSTER_PEASANT_GOBLIN_MALE_3_3) && vrng->random(30) < 100) pMonsterInfoUI_Doll.currentActionAnimation = ANIM_AtkMelee; pMonsterInfoUI_Doll.currentActionLength = 8 * @@ -1671,7 +1672,7 @@ void GameUI_DrawNPCPopup(void *_this) { // PopupWindowForBenefitAndJoinText popup_window.DrawTitleText(assets->pFontArrus.get(), 0, 12, colorTable.PaleCanary, NameAndTitle(pNPC), 3); popup_window.uFrameWidth -= 24; popup_window.uFrameZ = popup_window.uFrameX + popup_window.uFrameWidth - 1; - popup_window.DrawText(assets->pFontArrus.get(), {100, 36}, colorTable.White, BuildDialogueString(pText, 0, 0, HOUSE_INVALID, 0)); + popup_window.DrawText(assets->pFontArrus.get(), {100, 36}, colorTable.White, BuildDialogueString(pText, 0)); } } } diff --git a/src/Media/Audio/SoundEnums.h b/src/Media/Audio/SoundEnums.h index c66fc8e3cf1b..135b74c89bfd 100644 --- a/src/Media/Audio/SoundEnums.h +++ b/src/Media/Audio/SoundEnums.h @@ -1,5 +1,6 @@ #pragma once +// TODO(captainurist): #enum class + codegen enum SoundID { SOUND_Invalid = 0, SOUND_enter = 6, @@ -120,12 +121,14 @@ enum SoundID { SOUND_quest = 20001, }; +// TODO(captainurist): #enum class enum MusicID { MUSIC_Invalid = 0, MUSIC_MainMenu = 14, MUSIC_Credits = 15 }; +// TODO(captainurist): #enum class enum SOUND_TYPE { SOUND_TYPE_LEVEL = 0, SOUND_TYPE_SYSTEM = 1, @@ -134,6 +137,7 @@ enum SOUND_TYPE { SOUND_TYPE_LOCK = 4, }; +// TODO(captainurist): #enum class enum SOUND_FLAG { SOUND_FLAG_LOCKED = 0x1, SOUND_FLAG_3D = 0x2, diff --git a/test/Bin/GameTest/GameTests.cpp b/test/Bin/GameTest/GameTests.cpp index b782d4942e49..d2ef91c5e915 100644 --- a/test/Bin/GameTest/GameTests.cpp +++ b/test/Bin/GameTest/GameTests.cpp @@ -1011,7 +1011,7 @@ GAME_TEST(Issues, Issue675) { ItemGen item; for (int i = 0; i < 300; i++) { for (ItemTreasureLevel level : levels) { - pItemTable->generateItem(level, 0, &item); + pItemTable->generateItem(level, RANDOM_ITEM_ANY, &item); if (isPotion(item.uItemID)) { EXPECT_GE(item.potionPower, 1); EXPECT_FALSE(item.attributeEnchantment);