mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Add character screen model preview, item icons, stats panel, and fix targeting bugs
Enhanced the C-key character screen with a 3-column layout featuring a 3D character model preview (with drag-to-rotate), item icons loaded from BLP textures via ItemDisplayInfo.dbc, and a stats panel showing base + equipment bonuses. Fixed selection circle clipping under terrain by adding a Z offset, and corrected faction hostility logic that was wrongly marking hostile mobs as friendly.
This commit is contained in:
parent
7128ea1417
commit
394e91cd9e
12 changed files with 738 additions and 53 deletions
|
|
@ -835,6 +835,11 @@ void GameHandler::update(float deltaTime) {
|
|||
// Update combat text (Phase 2)
|
||||
updateCombatText(deltaTime);
|
||||
|
||||
// Update entity movement interpolation (keeps targeting in sync with visuals)
|
||||
for (auto& [guid, entity] : entityManager.getEntities()) {
|
||||
entity->updateMovement(deltaTime);
|
||||
}
|
||||
|
||||
// Single-player local combat
|
||||
if (singlePlayerMode_) {
|
||||
updateLocalCombat(deltaTime);
|
||||
|
|
@ -2480,6 +2485,11 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
if (it != block.fields.end() && it->second != 0) {
|
||||
auto unit = std::static_pointer_cast<Unit>(entity);
|
||||
unit->setEntry(it->second);
|
||||
// Set name from cache immediately if available
|
||||
std::string cached = getCachedCreatureName(it->second);
|
||||
if (!cached.empty()) {
|
||||
unit->setName(cached);
|
||||
}
|
||||
queryCreatureInfo(it->second, block.guid);
|
||||
}
|
||||
}
|
||||
|
|
@ -2493,6 +2503,7 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
case 25: unit->setPower(val); break;
|
||||
case 32: unit->setMaxHealth(val); break;
|
||||
case 33: unit->setMaxPower(val); break;
|
||||
case 55: unit->setFactionTemplate(val); break; // UNIT_FIELD_FACTIONTEMPLATE
|
||||
case 59: unit->setUnitFlags(val); break; // UNIT_FIELD_FLAGS
|
||||
case 54: unit->setLevel(val); break;
|
||||
case 67: unit->setDisplayId(val); break; // UNIT_FIELD_DISPLAYID
|
||||
|
|
@ -2500,6 +2511,10 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
default: break;
|
||||
}
|
||||
}
|
||||
// Determine hostility from faction template for online creatures
|
||||
if (unit->getFactionTemplate() != 0) {
|
||||
unit->setHostile(isHostileFaction(unit->getFactionTemplate()));
|
||||
}
|
||||
// Trigger creature spawn callback for units with displayId
|
||||
if (block.objectType == ObjectType::UNIT && unit->getDisplayId() != 0) {
|
||||
if (creatureSpawnCallback_) {
|
||||
|
|
@ -2591,6 +2606,10 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
case 33: unit->setMaxPower(val); break;
|
||||
case 59: unit->setUnitFlags(val); break; // UNIT_FIELD_FLAGS
|
||||
case 54: unit->setLevel(val); break;
|
||||
case 55: // UNIT_FIELD_FACTIONTEMPLATE
|
||||
unit->setFactionTemplate(val);
|
||||
unit->setHostile(isHostileFaction(val));
|
||||
break;
|
||||
case 82: unit->setNpcFlags(val); break; // UNIT_NPC_FLAGS
|
||||
default: break;
|
||||
}
|
||||
|
|
@ -3191,8 +3210,9 @@ void GameHandler::handleMonsterMove(network::Packet& packet) {
|
|||
}
|
||||
}
|
||||
|
||||
// Set entity to destination for targeting/logic; renderer interpolates visually
|
||||
entity->setPosition(destCanonical.x, destCanonical.y, destCanonical.z, orientation);
|
||||
// Interpolate entity position alongside renderer (so targeting matches visual)
|
||||
entity->startMoveTo(destCanonical.x, destCanonical.y, destCanonical.z,
|
||||
orientation, data.duration / 1000.0f);
|
||||
|
||||
// Notify renderer to smoothly move the creature
|
||||
if (creatureMoveCallback_) {
|
||||
|
|
|
|||
|
|
@ -497,6 +497,8 @@ std::vector<NpcSpawnDef> NpcManager::loadSpawnDefsFromAzerothCoreDb(
|
|||
uint32_t level = 1;
|
||||
uint32_t health = 100;
|
||||
std::string m2Path;
|
||||
uint32_t faction = 0;
|
||||
uint32_t npcFlags = 0;
|
||||
};
|
||||
std::unordered_map<uint32_t, TemplateRow> templates;
|
||||
|
||||
|
|
@ -546,20 +548,24 @@ std::vector<NpcSpawnDef> NpcManager::loadSpawnDefsFromAzerothCoreDb(
|
|||
}
|
||||
};
|
||||
|
||||
// Parse creature_template.sql: entry, modelid1(displayId), name, minlevel.
|
||||
// Parse creature_template.sql: entry, modelid1(displayId), name, minlevel, faction, npcflag.
|
||||
{
|
||||
std::ifstream in(tmplPath);
|
||||
processInsertStatements(in, [&](const std::vector<std::string>& cols) {
|
||||
if (cols.size() < 16) return true;
|
||||
if (cols.size() < 19) return true;
|
||||
try {
|
||||
uint32_t entry = static_cast<uint32_t>(std::stoul(cols[0]));
|
||||
uint32_t displayId = static_cast<uint32_t>(std::stoul(cols[6]));
|
||||
std::string name = unquoteSqlString(cols[10]);
|
||||
uint32_t minLevel = static_cast<uint32_t>(std::stoul(cols[14]));
|
||||
uint32_t faction = static_cast<uint32_t>(std::stoul(cols[17]));
|
||||
uint32_t npcflag = static_cast<uint32_t>(std::stoul(cols[18]));
|
||||
TemplateRow tr;
|
||||
tr.name = name.empty() ? ("Creature " + std::to_string(entry)) : name;
|
||||
tr.level = std::max(1u, minLevel);
|
||||
tr.health = 150 + tr.level * 35;
|
||||
tr.faction = faction;
|
||||
tr.npcFlags = npcflag;
|
||||
auto itModel = displayToModel.find(displayId);
|
||||
if (itModel != displayToModel.end()) {
|
||||
auto itPath = modelToPath.find(itModel->second);
|
||||
|
|
@ -604,6 +610,8 @@ std::vector<NpcSpawnDef> NpcManager::loadSpawnDefsFromAzerothCoreDb(
|
|||
def.level = it->second.level;
|
||||
def.health = std::max(it->second.health, curhealth);
|
||||
def.m2Path = it->second.m2Path;
|
||||
def.faction = it->second.faction;
|
||||
def.npcFlags = it->second.npcFlags;
|
||||
} else {
|
||||
def.entry = entry;
|
||||
def.name = "Creature " + std::to_string(entry);
|
||||
|
|
@ -709,6 +717,44 @@ void NpcManager::initialize(pipeline::AssetManager* am,
|
|||
}
|
||||
}
|
||||
|
||||
// Build faction hostility lookup from FactionTemplate.dbc.
|
||||
// Player is Alliance (Human) — faction template 1, friendGroup includes Alliance mask.
|
||||
// A creature is hostile if its enemyGroup overlaps the player's friendGroup.
|
||||
std::unordered_map<uint32_t, bool> factionHostile; // factionTemplateId → hostile to player
|
||||
{
|
||||
// FactionTemplate.dbc columns (3.3.5a):
|
||||
// 0: ID, 1: Faction, 2: Flags, 3: FactionGroup, 4: FriendGroup, 5: EnemyGroup,
|
||||
// 6-9: Enemies[4], 10-13: Friends[4]
|
||||
uint32_t playerFriendGroup = 0;
|
||||
if (auto dbc = am->loadDBC("FactionTemplate.dbc"); dbc && dbc->isLoaded()) {
|
||||
// First pass: find player faction template (ID 1) friendGroup
|
||||
for (uint32_t i = 0; i < dbc->getRecordCount(); i++) {
|
||||
if (dbc->getUInt32(i, 0) == 1) {
|
||||
playerFriendGroup = dbc->getUInt32(i, 4); // FriendGroup
|
||||
// Also include our own factionGroup as friendly
|
||||
playerFriendGroup |= dbc->getUInt32(i, 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Second pass: classify each faction template
|
||||
for (uint32_t i = 0; i < dbc->getRecordCount(); i++) {
|
||||
uint32_t id = dbc->getUInt32(i, 0);
|
||||
uint32_t enemyGroup = dbc->getUInt32(i, 5);
|
||||
uint32_t friendGroup = dbc->getUInt32(i, 4);
|
||||
// Hostile if creature's enemy groups overlap player's faction/friend groups
|
||||
bool hostile = (enemyGroup & playerFriendGroup) != 0;
|
||||
// Friendly only if creature's friendGroup explicitly includes player's groups
|
||||
bool friendly = (friendGroup & playerFriendGroup) != 0;
|
||||
// Hostile if explicitly hostile, or if no explicit relationship at all
|
||||
factionHostile[id] = hostile ? true : (!friendly && enemyGroup == 0 && friendGroup == 0);
|
||||
}
|
||||
LOG_INFO("NpcManager: loaded ", dbc->getRecordCount(),
|
||||
" faction templates (playerFriendGroup=0x", std::hex, playerFriendGroup, std::dec, ")");
|
||||
} else {
|
||||
LOG_WARNING("NpcManager: FactionTemplate.dbc not available, all NPCs default to hostile");
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn each NPC instance
|
||||
for (const auto* sPtr : active) {
|
||||
const auto& s = *sPtr;
|
||||
|
|
@ -751,6 +797,12 @@ void NpcManager::initialize(pipeline::AssetManager* am,
|
|||
if (s.entry != 0) {
|
||||
unit->setEntry(s.entry);
|
||||
}
|
||||
unit->setNpcFlags(s.npcFlags);
|
||||
unit->setFactionTemplate(s.faction);
|
||||
|
||||
// Determine hostility from faction template
|
||||
auto fIt = factionHostile.find(s.faction);
|
||||
unit->setHostile(fIt != factionHostile.end() ? fIt->second : true);
|
||||
|
||||
// Store canonical WoW coordinates for targeting/server compatibility
|
||||
glm::vec3 spawnCanonical = core::coords::renderToCanonical(glPos);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue