2026-02-12 22:56:36 -08:00
|
|
|
#include "game/update_field_table.hpp"
|
|
|
|
|
#include "core/logger.hpp"
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
|
|
namespace wowee {
|
|
|
|
|
namespace game {
|
|
|
|
|
|
|
|
|
|
static const UpdateFieldTable* g_activeUpdateFieldTable = nullptr;
|
|
|
|
|
|
|
|
|
|
void setActiveUpdateFieldTable(const UpdateFieldTable* table) { g_activeUpdateFieldTable = table; }
|
|
|
|
|
const UpdateFieldTable* getActiveUpdateFieldTable() { return g_activeUpdateFieldTable; }
|
|
|
|
|
|
|
|
|
|
struct UFNameEntry {
|
|
|
|
|
const char* name;
|
|
|
|
|
UF field;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const UFNameEntry kUFNames[] = {
|
|
|
|
|
{"OBJECT_FIELD_ENTRY", UF::OBJECT_FIELD_ENTRY},
|
feat: propagate OBJECT_FIELD_SCALE_X through creature and GO spawn pipeline
Reads OBJECT_FIELD_SCALE_X (field 4, cross-expansion) from CREATE_OBJECT
update fields and passes it through the full creature and game object spawn
chain: game_handler callbacks → pending spawn structs → async load results
→ createInstance() calls. This gives boss giants, gnomes, children, and
other non-unit-scale NPCs correct visual size, and ensures scaled GOs
(e.g. large treasure chests, oversized plants) render at the server-specified
scale rather than always at 1.0.
- Added OBJECT_FIELD_SCALE_X to UF enum and all expansion update_fields.json
- Added float scale to CreatureSpawnCallback and GameObjectSpawnCallback
- Propagated scale through PendingCreatureSpawn, PreparedCreatureModel,
PendingGameObjectSpawn, PreparedGameObjectWMO
- Used scale in charRenderer/m2Renderer/wmoRenderer createInstance() calls
- Sanity-clamped raw float to [0.01, 100.0] range before use
2026-03-10 22:45:47 -07:00
|
|
|
{"OBJECT_FIELD_SCALE_X", UF::OBJECT_FIELD_SCALE_X},
|
2026-02-12 22:56:36 -08:00
|
|
|
{"UNIT_FIELD_TARGET_LO", UF::UNIT_FIELD_TARGET_LO},
|
|
|
|
|
{"UNIT_FIELD_TARGET_HI", UF::UNIT_FIELD_TARGET_HI},
|
2026-02-13 19:40:54 -08:00
|
|
|
{"UNIT_FIELD_BYTES_0", UF::UNIT_FIELD_BYTES_0},
|
2026-02-12 22:56:36 -08:00
|
|
|
{"UNIT_FIELD_HEALTH", UF::UNIT_FIELD_HEALTH},
|
|
|
|
|
{"UNIT_FIELD_POWER1", UF::UNIT_FIELD_POWER1},
|
|
|
|
|
{"UNIT_FIELD_MAXHEALTH", UF::UNIT_FIELD_MAXHEALTH},
|
|
|
|
|
{"UNIT_FIELD_MAXPOWER1", UF::UNIT_FIELD_MAXPOWER1},
|
|
|
|
|
{"UNIT_FIELD_LEVEL", UF::UNIT_FIELD_LEVEL},
|
|
|
|
|
{"UNIT_FIELD_FACTIONTEMPLATE", UF::UNIT_FIELD_FACTIONTEMPLATE},
|
|
|
|
|
{"UNIT_FIELD_FLAGS", UF::UNIT_FIELD_FLAGS},
|
|
|
|
|
{"UNIT_FIELD_FLAGS_2", UF::UNIT_FIELD_FLAGS_2},
|
|
|
|
|
{"UNIT_FIELD_DISPLAYID", UF::UNIT_FIELD_DISPLAYID},
|
|
|
|
|
{"UNIT_FIELD_MOUNTDISPLAYID", UF::UNIT_FIELD_MOUNTDISPLAYID},
|
Fix mount sounds, grey WMO meshes, taxi landing, tree animations, and classic dismount
- Per-family mount sounds (kodo, tallstrider, mechanostrider, etc.) detected from M2 model path
- Skip WMO groups with SHOW_SKYBOX flag or all-untextured batches (grey mesh in Orgrimmar)
- Freeze physics during taxi landing until terrain loads to prevent falling through void
- Disable bone animations on tropical vegetation (palm, bamboo, banana, etc.) to fix wiggling
- Snap player to final taxi waypoint on flight completion
- Extract mount aura spell ID from classic UNIT_FIELD_AURAS for CMSG_CANCEL_AURA dismount
- Increase /unstuck forward nudge to 5 units
2026-02-14 21:04:20 -08:00
|
|
|
{"UNIT_FIELD_AURAS", UF::UNIT_FIELD_AURAS},
|
2026-02-12 22:56:36 -08:00
|
|
|
{"UNIT_NPC_FLAGS", UF::UNIT_NPC_FLAGS},
|
|
|
|
|
{"UNIT_DYNAMIC_FLAGS", UF::UNIT_DYNAMIC_FLAGS},
|
2026-02-19 17:45:09 -08:00
|
|
|
{"UNIT_FIELD_RESISTANCES", UF::UNIT_FIELD_RESISTANCES},
|
2026-03-10 23:08:15 -07:00
|
|
|
{"UNIT_FIELD_STAT0", UF::UNIT_FIELD_STAT0},
|
|
|
|
|
{"UNIT_FIELD_STAT1", UF::UNIT_FIELD_STAT1},
|
|
|
|
|
{"UNIT_FIELD_STAT2", UF::UNIT_FIELD_STAT2},
|
|
|
|
|
{"UNIT_FIELD_STAT3", UF::UNIT_FIELD_STAT3},
|
|
|
|
|
{"UNIT_FIELD_STAT4", UF::UNIT_FIELD_STAT4},
|
2026-02-12 22:56:36 -08:00
|
|
|
{"UNIT_END", UF::UNIT_END},
|
|
|
|
|
{"PLAYER_FLAGS", UF::PLAYER_FLAGS},
|
2026-02-13 19:40:54 -08:00
|
|
|
{"PLAYER_BYTES", UF::PLAYER_BYTES},
|
|
|
|
|
{"PLAYER_BYTES_2", UF::PLAYER_BYTES_2},
|
2026-02-12 22:56:36 -08:00
|
|
|
{"PLAYER_XP", UF::PLAYER_XP},
|
|
|
|
|
{"PLAYER_NEXT_LEVEL_XP", UF::PLAYER_NEXT_LEVEL_XP},
|
|
|
|
|
{"PLAYER_FIELD_COINAGE", UF::PLAYER_FIELD_COINAGE},
|
|
|
|
|
{"PLAYER_QUEST_LOG_START", UF::PLAYER_QUEST_LOG_START},
|
|
|
|
|
{"PLAYER_FIELD_INV_SLOT_HEAD", UF::PLAYER_FIELD_INV_SLOT_HEAD},
|
|
|
|
|
{"PLAYER_FIELD_PACK_SLOT_1", UF::PLAYER_FIELD_PACK_SLOT_1},
|
Implement bank, guild bank, and auction house systems
Add 27 new opcodes, packet builders/parsers, handler methods, inventory
extension with 28 bank slots + 7 bank bags, and UI windows for personal
bank, guild bank (6 tabs x 98 slots), and auction house (browse/sell/bid).
Fix Classic gossip parser to omit boxMoney/boxText fields not present in
Vanilla protocol, fix gossip icon labels with text-based NPC type detection,
and add Turtle WoW opcode mappings for bank and auction interactions.
2026-02-16 21:11:18 -08:00
|
|
|
{"PLAYER_FIELD_BANK_SLOT_1", UF::PLAYER_FIELD_BANK_SLOT_1},
|
|
|
|
|
{"PLAYER_FIELD_BANKBAG_SLOT_1", UF::PLAYER_FIELD_BANKBAG_SLOT_1},
|
2026-02-12 22:56:36 -08:00
|
|
|
{"PLAYER_SKILL_INFO_START", UF::PLAYER_SKILL_INFO_START},
|
|
|
|
|
{"PLAYER_EXPLORED_ZONES_START", UF::PLAYER_EXPLORED_ZONES_START},
|
|
|
|
|
{"GAMEOBJECT_DISPLAYID", UF::GAMEOBJECT_DISPLAYID},
|
|
|
|
|
{"ITEM_FIELD_STACK_COUNT", UF::ITEM_FIELD_STACK_COUNT},
|
feat: propagate OBJECT_FIELD_SCALE_X through creature and GO spawn pipeline
Reads OBJECT_FIELD_SCALE_X (field 4, cross-expansion) from CREATE_OBJECT
update fields and passes it through the full creature and game object spawn
chain: game_handler callbacks → pending spawn structs → async load results
→ createInstance() calls. This gives boss giants, gnomes, children, and
other non-unit-scale NPCs correct visual size, and ensures scaled GOs
(e.g. large treasure chests, oversized plants) render at the server-specified
scale rather than always at 1.0.
- Added OBJECT_FIELD_SCALE_X to UF enum and all expansion update_fields.json
- Added float scale to CreatureSpawnCallback and GameObjectSpawnCallback
- Propagated scale through PendingCreatureSpawn, PreparedCreatureModel,
PendingGameObjectSpawn, PreparedGameObjectWMO
- Used scale in charRenderer/m2Renderer/wmoRenderer createInstance() calls
- Sanity-clamped raw float to [0.01, 100.0] range before use
2026-03-10 22:45:47 -07:00
|
|
|
{"ITEM_FIELD_DURABILITY", UF::ITEM_FIELD_DURABILITY},
|
|
|
|
|
{"ITEM_FIELD_MAXDURABILITY", UF::ITEM_FIELD_MAXDURABILITY},
|
|
|
|
|
{"PLAYER_REST_STATE_EXPERIENCE", UF::PLAYER_REST_STATE_EXPERIENCE},
|
2026-02-13 22:14:34 -08:00
|
|
|
{"CONTAINER_FIELD_NUM_SLOTS", UF::CONTAINER_FIELD_NUM_SLOTS},
|
|
|
|
|
{"CONTAINER_FIELD_SLOT_1", UF::CONTAINER_FIELD_SLOT_1},
|
2026-02-12 22:56:36 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static constexpr size_t kUFNameCount = sizeof(kUFNames) / sizeof(kUFNames[0]);
|
|
|
|
|
|
|
|
|
|
bool UpdateFieldTable::loadFromJson(const std::string& path) {
|
|
|
|
|
std::ifstream f(path);
|
|
|
|
|
if (!f.is_open()) {
|
|
|
|
|
LOG_WARNING("UpdateFieldTable: cannot open ", path);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string json((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
|
|
|
|
|
|
|
|
|
fieldMap_.clear();
|
|
|
|
|
size_t loaded = 0;
|
|
|
|
|
size_t pos = 0;
|
|
|
|
|
|
|
|
|
|
while (pos < json.size()) {
|
|
|
|
|
size_t keyStart = json.find('"', pos);
|
|
|
|
|
if (keyStart == std::string::npos) break;
|
|
|
|
|
size_t keyEnd = json.find('"', keyStart + 1);
|
|
|
|
|
if (keyEnd == std::string::npos) break;
|
|
|
|
|
std::string key = json.substr(keyStart + 1, keyEnd - keyStart - 1);
|
|
|
|
|
|
|
|
|
|
size_t colon = json.find(':', keyEnd);
|
|
|
|
|
if (colon == std::string::npos) break;
|
|
|
|
|
|
|
|
|
|
size_t valStart = colon + 1;
|
|
|
|
|
while (valStart < json.size() && (json[valStart] == ' ' || json[valStart] == '\t' ||
|
|
|
|
|
json[valStart] == '\r' || json[valStart] == '\n'))
|
|
|
|
|
++valStart;
|
|
|
|
|
|
|
|
|
|
size_t valEnd = json.find_first_of(",}\r\n", valStart);
|
|
|
|
|
if (valEnd == std::string::npos) valEnd = json.size();
|
|
|
|
|
std::string valStr = json.substr(valStart, valEnd - valStart);
|
|
|
|
|
// Trim whitespace
|
|
|
|
|
while (!valStr.empty() && (valStr.back() == ' ' || valStr.back() == '\t'))
|
|
|
|
|
valStr.pop_back();
|
|
|
|
|
|
|
|
|
|
uint16_t idx = 0;
|
|
|
|
|
try { idx = static_cast<uint16_t>(std::stoul(valStr)); } catch (...) {
|
|
|
|
|
pos = valEnd + 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find matching UF enum
|
|
|
|
|
for (size_t i = 0; i < kUFNameCount; ++i) {
|
|
|
|
|
if (key == kUFNames[i].name) {
|
|
|
|
|
fieldMap_[static_cast<uint16_t>(kUFNames[i].field)] = idx;
|
|
|
|
|
++loaded;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pos = valEnd + 1;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-13 16:53:28 -08:00
|
|
|
if (loaded == 0) {
|
2026-02-20 00:39:20 -08:00
|
|
|
LOG_WARNING("UpdateFieldTable: no fields loaded from ", path);
|
2026-02-13 16:53:28 -08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-12 22:56:36 -08:00
|
|
|
LOG_INFO("UpdateFieldTable: loaded ", loaded, " fields from ", path);
|
2026-02-13 16:53:28 -08:00
|
|
|
return true;
|
2026-02-12 22:56:36 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint16_t UpdateFieldTable::index(UF field) const {
|
|
|
|
|
auto it = fieldMap_.find(static_cast<uint16_t>(field));
|
|
|
|
|
return (it != fieldMap_.end()) ? it->second : 0xFFFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UpdateFieldTable::hasField(UF field) const {
|
|
|
|
|
return fieldMap_.count(static_cast<uint16_t>(field)) > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace game
|
|
|
|
|
} // namespace wowee
|