#include "StdAfx.h" #include "WeenieObject.h" #include "PhysicsObj.h" #include "Monster.h" #include "Player.h" #include "Appraisal.h" #include "PartArray.h" //Network access. #include "Client.h" #include "BinaryWriter.h" #include "ObjectMsgs.h" #include "ClientCommands.h" #define MESSAGE_BEGIN(x) BinaryWriter *x = new BinaryWriter #define MESSAGE_END(x) return x BinaryWriter *GetWeenieObjData(std::shared_ptr pEntity) { BinaryWriter *pWriter = new BinaryWriter; PublicWeenieDesc *pDesc = PublicWeenieDesc::CreateFromWeenie(pEntity); pDesc->Pack(pWriter); delete pDesc; return pWriter; #if 0 BinaryWriter OptionalWeenieObjData; DWORD dwSections = 0; if (pEntity->Container_HasContents()) { dwSections |= PublicWeenieDescPackHeader::PWD_Packed_ItemsCapacity; OptionalWeenieObjData.Write(pEntity->Container_MaxInventorySlots()); // item capacity dwSections |= PublicWeenieDescPackHeader::PWD_Packed_ContainersCapacity; OptionalWeenieObjData.Write(pEntity->Container_MaxContainerSlots()); // container capacity } int value = 0; if (pEntity->m_Qualities.InqInt(VALUE_INT, value, FALSE, FALSE)) { dwSections |= PublicWeenieDescPackHeader::PWD_Packed_Value; OptionalWeenieObjData.Write(value); } int useability = 0; if (pEntity->m_Qualities.InqInt(ITEM_USEABLE_INT, useability)) { dwSections |= PublicWeenieDescPackHeader::PWD_Packed_Useability; OptionalWeenieObjData.Write(useability); } double useRadius; if (pEntity->m_Qualities.InqFloat(USE_RADIUS_FLOAT, useRadius)) { dwSections |= PublicWeenieDescPackHeader::PWD_Packed_UseRadius; OptionalWeenieObjData.Write(useRadius); } if (pEntity->m_TargetType != 0) { dwSections |= PublicWeenieDescPackHeader::PWD_Packed_TargetType; OptionalWeenieObjData.Write(pEntity->m_TargetType); } if (pEntity->m_UIEffects) { dwSections |= PublicWeenieDescPackHeader::PWD_Packed_UIEffects; OptionalWeenieObjData.Write(pEntity->m_UIEffects); } if (pEntity->HasEquipType()) { dwSections |= PublicWeenieDescPackHeader::PWD_Packed_CombatUse; OptionalWeenieObjData.Write((BYTE)pEntity->GetEquipType()); } if (pEntity->IsContained()) { dwSections |= PublicWeenieDescPackHeader::PWD_Packed_ContainerID; OptionalWeenieObjData.Write(pEntity->GetContainerID()); } if (pEntity->HasCoverage()) { CBaseItem *pItem = (CBaseItem*)pEntity; DWORD dwCoverage1 = pItem->GetCoverage1(); DWORD dwCoverage2 = pItem->GetCoverage2(); DWORD dwCoverage3 = pItem->GetCoverage3(); if (dwCoverage1) { dwSections |= PublicWeenieDescPackHeader::PWD_Packed_ValidLocations; OptionalWeenieObjData.Write(dwCoverage1); } if (dwCoverage2) { dwSections |= PublicWeenieDescPackHeader::PWD_Packed_Location; OptionalWeenieObjData.Write(dwCoverage2); } if (dwCoverage3) { dwSections |= PublicWeenieDescPackHeader::PWD_Packed_Priority; OptionalWeenieObjData.Write(dwCoverage3); } } int blipColor = 0; if (pEntity->m_Qualities.InqInt(RADARBLIP_COLOR_INT, blipColor)) { dwSections |= PublicWeenieDescPackHeader::PWD_Packed_BlipColor; OptionalWeenieObjData.Write(blipColor); } int showRadar; if (pEntity->m_Qualities.InqInt(SHOWABLE_ON_RADAR_INT, showRadar)) { dwSections |= PublicWeenieDescPackHeader::PWD_Packed_RadarEnum; OptionalWeenieObjData.Write((BYTE)showRadar); } int burden = 0; if (pEntity->m_Qualities.InqInt(ENCUMB_VAL_INT, value, FALSE, FALSE)) { dwSections |= PublicWeenieDescPackHeader::PWD_Packed_Burden; OptionalWeenieObjData.Write(burden); } WORD spell_id = (WORD)pEntity->GetSpellID(); if (spell_id) { dwSections |= PublicWeenieDescPackHeader::PWD_Packed_SpellID; OptionalWeenieObjData.Write(spell_id); } BinaryWriter *WeenieObjData = new BinaryWriter; WeenieObjData->Write(dwSections); WeenieObjData->WriteString(pEntity->GetName().c_str()); WeenieObjData->WritePackedDWORD(pEntity->m_Qualities.id); WeenieObjData->Pack_AsDataIDOfKnownType(0x06000000, 0x06000000 | pEntity->GetIcon()); WeenieObjData->Write(pEntity->GetItemType()); WeenieObjData->Write(pEntity->m_WeenieBitfield); WeenieObjData->Align(); WeenieObjData->Write(&OptionalWeenieObjData); return WeenieObjData; #endif } BinaryWriter *GetPhysicsObjData(std::shared_ptr pEntity) { BinaryWriter OptionalPhysicsObjData; DWORD dwSections = 0; //0x019803 if (pEntity->movement_manager) { dwSections |= PhysicsDescInfo::MOVEMENT; BinaryWriter *AnimInfo = pEntity->Animation_GetAnimationInfo(); OptionalPhysicsObjData.Write(AnimInfo->GetSize()); OptionalPhysicsObjData.Write(AnimInfo); OptionalPhysicsObjData.Write(pEntity->last_move_was_autonomous); // autonomous movement? delete AnimInfo; } else { dwSections |= PhysicsDescInfo::ANIMFRAME_ID; OptionalPhysicsObjData.Write(pEntity->GetPlacementFrameID()); } if (!pEntity->HasOwner()) { dwSections |= PhysicsDescInfo::POSITION; OptionalPhysicsObjData.Write(&pEntity->m_Position); } DWORD motion_table = 0; if (pEntity->m_Qualities.InqDataID(MOTION_TABLE_DID, motion_table) && motion_table) { dwSections |= PhysicsDescInfo::MTABLE; OptionalPhysicsObjData.Write(motion_table); } DWORD sound_table = 0; if (pEntity->m_Qualities.InqDataID(SOUND_TABLE_DID, sound_table) && sound_table) { dwSections |= PhysicsDescInfo::STABLE; OptionalPhysicsObjData.Write(sound_table); } DWORD pe_table = 0; if (pEntity->m_Qualities.InqDataID(PHYSICS_EFFECT_TABLE_DID, pe_table) && pe_table) { dwSections |= PhysicsDescInfo::PETABLE; OptionalPhysicsObjData.Write(pe_table); } DWORD setup_id = 0; if (pEntity->m_Qualities.InqDataID(SETUP_DID, setup_id) && setup_id) { dwSections |= PhysicsDescInfo::CSETUP; OptionalPhysicsObjData.Write(setup_id); } if (std::shared_ptr pParent = pEntity->parent.lock()) { dwSections |= PhysicsDescInfo::PARENT; OptionalPhysicsObjData.Write(pParent->id); OptionalPhysicsObjData.Write(pEntity->InqIntQuality(PARENT_LOCATION_INT, 0)); } if (pEntity->children && pEntity->children->num_objects) { dwSections |= PhysicsDescInfo::CHILDREN; OptionalPhysicsObjData.Write(pEntity->children->num_objects); for (DWORD i = 0; i < pEntity->children->num_objects; i++) { if (std::shared_ptr pChild = pEntity->children->objects.array_data[i].lock()) { OptionalPhysicsObjData.Write(pChild->id); OptionalPhysicsObjData.Write(pEntity->children->location_ids.array_data[i]); } } } if (pEntity->m_scale != 0.0f && pEntity->m_scale != 1.0f) { dwSections |= PhysicsDescInfo::OBJSCALE; OptionalPhysicsObjData.Write(pEntity->m_scale); } if (pEntity->m_fFriction != CWeenieObject::DEFAULT_FRICTION) { dwSections |= PhysicsDescInfo::FRICTION; OptionalPhysicsObjData.Write(pEntity->m_fFriction); } if (pEntity->m_fElasticity != CWeenieObject::DEFAULT_ELASTICITY) { dwSections |= PhysicsDescInfo::ELASTICITY; OptionalPhysicsObjData.Write(pEntity->m_fElasticity); } if (pEntity->m_fTranslucency != CWeenieObject::DEFAULT_TRANSLUCENCY) { dwSections |= PhysicsDescInfo::TRANSLUCENCY; OptionalPhysicsObjData.Write(pEntity->m_fTranslucency); } if (pEntity->m_velocityVector != Vector(0, 0, 0)) { dwSections |= PhysicsDescInfo::VELOCITY; pEntity->m_velocityVector.Pack(&OptionalPhysicsObjData); } if (pEntity->m_Acceleration != Vector(0, 0, 0)) { dwSections |= PhysicsDescInfo::ACCELERATION; pEntity->m_Acceleration.Pack(&OptionalPhysicsObjData); } if (pEntity->m_Omega != Vector(0, 0, 0)) { dwSections |= PhysicsDescInfo::OMEGA; pEntity->m_Omega.Pack(&OptionalPhysicsObjData); } if (pEntity->m_DefaultScript != 0) { dwSections |= PhysicsDescInfo::DEFAULT_SCRIPT; OptionalPhysicsObjData.Write(pEntity->m_DefaultScript); } if (pEntity->m_DefaultScriptIntensity != 0) { dwSections |= PhysicsDescInfo::DEFAULT_SCRIPT_INTENSITY; OptionalPhysicsObjData.Write(pEntity->m_DefaultScriptIntensity); } BinaryWriter *PhysicsObjData = new BinaryWriter; PhysicsObjData->Write(dwSections); PhysicsObjData->Write(pEntity->m_PhysicsState); //VIS flags PhysicsObjData->Write(&OptionalPhysicsObjData); //Moved from CO PhysicsObjData->Write(pEntity->_position_timestamp); PhysicsObjData->Write(pEntity->_movement_timestamp); PhysicsObjData->Write(pEntity->_state_timestamp); PhysicsObjData->Write(pEntity->_vector_timestamp); PhysicsObjData->Write(pEntity->_teleport_timestamp); PhysicsObjData->Write(pEntity->_server_control_timestamp); PhysicsObjData->Write(pEntity->_force_position_timestamp); PhysicsObjData->Write(pEntity->_objdesc_timestamp); PhysicsObjData->Write(pEntity->_instance_timestamp); PhysicsObjData->Align(); return PhysicsObjData; } BinaryWriter *CreateObject(std::shared_ptr pEntity) { BinaryWriter *CO = new BinaryWriter; CO->Write(0xF745); CO->Write(pEntity->GetID()); ObjDesc objDesc; pEntity->GetObjDesc(objDesc); objDesc.Pack(CO); BinaryWriter *PhysicsObjData = GetPhysicsObjData(pEntity); CO->Write(PhysicsObjData); delete PhysicsObjData; BinaryWriter *WeenieObjData = GetWeenieObjData(pEntity); CO->Write(WeenieObjData); delete WeenieObjData; CO->Align(); return CO; } BinaryWriter *UpdateObject(std::shared_ptr pEntity) { BinaryWriter *UO = new BinaryWriter; UO->Write(0xF7DB); UO->Write(pEntity->GetID()); ObjDesc objDesc; pEntity->GetObjDesc(objDesc); objDesc.Pack(UO); pEntity->_objdesc_timestamp++; pEntity->_teleport_timestamp++; pEntity->_movement_timestamp++; pEntity->_state_timestamp++; pEntity->_vector_timestamp++; pEntity->_position_timestamp++; pEntity->_server_control_timestamp++; pEntity->_force_position_timestamp++; pEntity->_objdesc_timestamp++; BinaryWriter *PhysicsObjData = GetPhysicsObjData(pEntity); UO->Write(PhysicsObjData); delete PhysicsObjData; BinaryWriter *WeenieObjData = GetWeenieObjData(pEntity); UO->Write(WeenieObjData); delete WeenieObjData; UO->Align(); return UO; } BinaryWriter *IdentifyObjectFail(std::shared_ptr pEntity, bool bShowLevel) { BinaryWriter *pWriter = new BinaryWriter; pWriter->Write(0xC9); // message ID pWriter->Write(pEntity->GetID()); AppraisalProfile profile; profile.success_flag = FALSE; if (pEntity->m_Qualities.m_IntStats) { profile._intStatsTable = new PackableHashTableWithJson(); *profile._intStatsTable = *pEntity->m_Qualities.m_IntStats; for (PackableHashTableWithJson::iterator i = profile._intStatsTable->begin(); i != profile._intStatsTable->end(); ) { switch (i->first) { case CREATURE_TYPE_INT: case ENCUMB_VAL_INT: case PLAYER_KILLER_STATUS_INT: i++; continue; } if (i->first == LEVEL_INT && bShowLevel) { i++; continue; } i = profile._intStatsTable->erase(i); } if (pEntity->m_Qualities._enchantment_reg) { for (auto &entry : *profile._intStatsTable) { pEntity->m_Qualities._enchantment_reg->EnchantInt(entry.first, &entry.second, FALSE); } } if (profile._intStatsTable->empty()) { delete profile._intStatsTable; profile._intStatsTable = NULL; } } if (pEntity->m_Qualities.m_BoolStats) { profile._boolStatsTable = new PackableHashTableWithJson(); *profile._boolStatsTable = *pEntity->m_Qualities.m_BoolStats; for (PackableHashTableWithJson::iterator i = profile._boolStatsTable->begin(); i != profile._boolStatsTable->end(); ) { switch (i->first) { case OPEN_BOOL: case LOCKED_BOOL: i++; continue; } i = profile._boolStatsTable->erase(i); } if (profile._boolStatsTable->empty()) { delete profile._boolStatsTable; profile._boolStatsTable = NULL; } } if (pEntity->m_Qualities.m_StringStats) { profile._strStatsTable = new PackableHashTableWithJson(); *profile._strStatsTable = *pEntity->m_Qualities.m_StringStats; for (PackableHashTableWithJson::iterator i = profile._strStatsTable->begin(); i != profile._strStatsTable->end(); ) { switch (i->first) { case USE_STRING: case TEMPLATE_STRING: i++; continue; } i = profile._strStatsTable->erase(i); } if (profile._strStatsTable->empty()) { delete profile._strStatsTable; profile._strStatsTable = NULL; } } if (pEntity->m_Qualities.m_DIDStats) { profile._didStatsTable = new PackableHashTableWithJson(); *profile._didStatsTable = *pEntity->m_Qualities.m_DIDStats; for (PackableHashTableWithJson::iterator i = profile._didStatsTable->begin(); i != profile._didStatsTable->end(); ) { switch (i->first) { case EYES_PALETTE_DID: case EYES_TEXTURE_DID: case HAIR_PALETTE_DID: case NOSE_TEXTURE_DID: case SKIN_PALETTE_DID: case MOUTH_TEXTURE_DID: i++; continue; } i = profile._didStatsTable->erase(i); } if (profile._didStatsTable->empty()) { delete profile._didStatsTable; profile._didStatsTable = NULL; } } if (pEntity->IsCreature()) { profile.creature_profile = new CreatureAppraisalProfile(); auto setCreatureProfileAttribute2nd = [&](STypeAttribute2nd attrib2nd, DWORD &value, DWORD &bitfield) { DWORD raw = 0; pEntity->m_Qualities.InqAttribute2nd(attrib2nd, raw, TRUE); pEntity->m_Qualities.InqAttribute2nd(attrib2nd, value, FALSE); if (raw != value) { switch (attrib2nd) { case MAX_HEALTH_ATTRIBUTE_2ND: bitfield |= CreatureAppraisalProfile::BF_MAX_HEALTH; break; case MAX_STAMINA_ATTRIBUTE_2ND: bitfield |= CreatureAppraisalProfile::BF_MAX_STAMINA; break; case MAX_MANA_ATTRIBUTE_2ND: bitfield |= CreatureAppraisalProfile::BF_MAX_MANA; break; } if (value > raw) { switch (attrib2nd) { case MAX_HEALTH_ATTRIBUTE_2ND: bitfield |= CreatureAppraisalProfile::BF_MAX_HEALTH_HI; break; case MAX_STAMINA_ATTRIBUTE_2ND: bitfield |= CreatureAppraisalProfile::BF_MAX_STAMINA_HI; break; case MAX_MANA_ATTRIBUTE_2ND: bitfield |= CreatureAppraisalProfile::BF_MAX_MANA_HI; break; } } } }; setCreatureProfileAttribute2nd(HEALTH_ATTRIBUTE_2ND, profile.creature_profile->health, profile.creature_profile->enchantment_bitfield); setCreatureProfileAttribute2nd(MAX_HEALTH_ATTRIBUTE_2ND, profile.creature_profile->max_health, profile.creature_profile->enchantment_bitfield); } pWriter->Write(&profile); return pWriter; } BinaryWriter *IdentifyObject(std::shared_ptr pSource, std::shared_ptr pEntity, DWORD overrideId) { BinaryWriter *pWriter = new BinaryWriter; pWriter->Write(0xC9); // message ID if (overrideId) pWriter->Write(overrideId); else pWriter->Write(pEntity->GetID()); AppraisalProfile profile; // always true for now profile.success_flag = TRUE; BOOL bIsSourceAdmin = FALSE; BOOL bIsPlayer = FALSE; DWORD characterOptions; DWORD characterOptions2; if (std::shared_ptr pPlayerSource = pSource->AsPlayer()) { bIsSourceAdmin = pPlayerSource->GetAccessLevel() >= ADMIN_ACCESS; } if (std::shared_ptr pPlayer = pEntity->AsPlayer()) { bIsPlayer = true; characterOptions = pPlayer->GetCharacterOptions(); characterOptions2 = pPlayer->GetCharacterOptions2(); } if (pEntity->m_Qualities.m_IntStats) { profile._intStatsTable = new PackableHashTableWithJson(); *profile._intStatsTable = *pEntity->m_Qualities.m_IntStats; profile._intStatsTable->remove(PHYSICS_STATE_INT); profile._intStatsTable->remove(XP_OVERRIDE_INT); profile._intStatsTable->remove(MAX_GENERATED_OBJECTS_INT); //profile._intStatsTable->remove(NUM_ITEMS_IN_MATERIAL_INT); // ADDED -- removed the mysterious double used for calculations (divison against amt of items in bag) //[NUM_ITEMS_IN_MATERIAL_INT(170)] == Correct one to keep for SHOWING bags' workmanship //[STRUCTURE_INT (92)] == Number of Uses on Salvage ID...actually Double SalvageWorkmanship (SHOWN) //----not sure where to change msg prepended to it. if (bIsPlayer) { if (!(characterOptions2 & DisplayAge_CharacterOptions2)) profile._intStatsTable->remove(AGE_INT); if (!(characterOptions2 & DisplayNumberDeaths_CharacterOptions2)) profile._intStatsTable->remove(NUM_DEATHS_INT); } if (pEntity->AsCaster()) { profile._intStatsTable->remove(DAMAGE_INT); } if (pEntity->m_Qualities._enchantment_reg) { for (auto &entry : *profile._intStatsTable) { pEntity->m_Qualities._enchantment_reg->EnchantInt(entry.first, &entry.second, FALSE); } } if (int skillActivationType = (int)pEntity->m_Qualities.GetDID(ITEM_SKILL_LIMIT_DID, 0)) { profile._intStatsTable->add(APPRAISAL_ITEM_SKILL_INT, &skillActivationType); } int* shieldLoc = profile._intStatsTable->lookup(LOCATIONS_INT); if (shieldLoc && *shieldLoc == static_cast(INVENTORY_LOC::SHIELD_LOC)) { SKILL_ADVANCEMENT_CLASS sac = SKILL_ADVANCEMENT_CLASS::UNTRAINED_SKILL_ADVANCEMENT_CLASS; pSource->m_Qualities.InqSkillAdvancementClass(SHIELD_SKILL, sac); if (sac == SPECIALIZED_SKILL_ADVANCEMENT_CLASS) { profile._intStatsTable->add(SHIELD_VALUE_INT, profile._intStatsTable->lookup(ARMOR_LEVEL_INT)); } else { int *halfShieldValue = profile._intStatsTable->lookup(ARMOR_LEVEL_INT); int halfTrueValue = *halfShieldValue; halfTrueValue /= 2; profile._intStatsTable->add(SHIELD_VALUE_INT, &halfTrueValue); } } } if (pEntity->m_Qualities.m_Int64Stats) { profile._int64StatsTable = new PackableHashTableWithJson(); *profile._int64StatsTable = *pEntity->m_Qualities.m_Int64Stats; } if (pEntity->m_Qualities.m_BoolStats) { profile._boolStatsTable = new PackableHashTableWithJson(); *profile._boolStatsTable = *pEntity->m_Qualities.m_BoolStats; profile._boolStatsTable->remove(REPORT_COLLISIONS_AS_ENVIRONMENT_BOOL); profile._boolStatsTable->remove(ATTACKABLE_BOOL); profile._boolStatsTable->remove(IS_HOT_BOOL); profile._boolStatsTable->remove(AFFECTS_AIS_BOOL); profile._boolStatsTable->remove(UI_HIDDEN_BOOL); profile._boolStatsTable->remove(GRAVITY_STATUS_BOOL); profile._boolStatsTable->remove(ETHEREAL_BOOL); profile._boolStatsTable->remove(IGNORE_COLLISIONS_BOOL); profile._boolStatsTable->remove(REPORT_COLLISIONS_BOOL); profile._boolStatsTable->remove(STUCK_BOOL); profile._boolStatsTable->remove(VISIBILITY_BOOL); profile._boolStatsTable->remove(LIGHTS_STATUS_BOOL); } if (pEntity->m_Qualities.m_FloatStats) { profile._floatStatsTable = new PackableHashTableWithJson(); *profile._floatStatsTable = *pEntity->m_Qualities.m_FloatStats; profile._floatStatsTable->remove(HEARTBEAT_INTERVAL_FLOAT); profile._floatStatsTable->remove(HEARTBEAT_TIMESTAMP_FLOAT); profile._floatStatsTable->remove(OBVIOUS_RADAR_RANGE_FLOAT); profile._floatStatsTable->remove(HOTSPOT_CYCLE_TIME_FLOAT); profile._floatStatsTable->remove(HOTSPOT_CYCLE_TIME_VARIANCE_FLOAT); profile._floatStatsTable->remove(DEFAULT_SCALE_FLOAT); profile._floatStatsTable->remove(RESET_INTERVAL_FLOAT); profile._floatStatsTable->remove(REGENERATION_INTERVAL_FLOAT); profile._floatStatsTable->remove(USE_RADIUS_FLOAT); double *weapon_defense = profile._floatStatsTable->lookup(WEAPON_DEFENSE_FLOAT); double old_weapon_defense = weapon_defense ? *weapon_defense : 1.0; if (pEntity->m_Qualities._enchantment_reg) { for (auto &entry : *profile._floatStatsTable) { pEntity->m_Qualities._enchantment_reg->EnchantFloat(entry.first, &entry.second); } } // special case scenarios for item buffs... if (weapon_defense) { *weapon_defense = pEntity->GetMeleeDefenseMod(); if (fabs(*weapon_defense - old_weapon_defense) >= F_EPSILON) { profile.weapon_ench_bitfield |= BF_WEAPON_DEFENSE; if (*weapon_defense > old_weapon_defense) profile.weapon_ench_bitfield |= BF_WEAPON_DEFENSE_HI; } } } if (pEntity->m_Qualities.m_StringStats) { profile._strStatsTable = new PackableHashTableWithJson(); *profile._strStatsTable = *pEntity->m_Qualities.m_StringStats; if (bIsPlayer) { if (!(characterOptions2 & DisplayDateOfBirth_CharacterOptions2)) profile._strStatsTable->remove(DATE_OF_BIRTH_STRING); } profile._strStatsTable->remove(KEY_CODE_STRING); profile._strStatsTable->remove(LOCK_CODE_STRING); profile._strStatsTable->remove(QUEST_STRING); profile._strStatsTable->remove(QUEST_RESTRICTION_STRING); profile._strStatsTable->remove(ACTIVATION_TALK_STRING); profile._strStatsTable->remove(ACTIVATION_FAILURE_STRING); profile._strStatsTable->remove(USE_MESSAGE_STRING); profile._strStatsTable->remove(GENERATOR_EVENT_STRING); profile._strStatsTable->remove(KILL_QUEST_STRING); if (false) // bIsSourceAdmin) { /* profile._strStatsTable->add(LONG_DESC_STRING, csprintf( "For debug purposes: 0x%08X %u\nWT: %d\n%d", pEntity->GetID(), pEntity->m_Qualities.GetID(), pEntity->m_Qualities.m_WeenieType, pEntity->m_Qualities.GetInt(ITEM_TYPE_INT, 0))); */ } } if (pEntity->m_Qualities.m_DIDStats) { profile._didStatsTable = new PackableHashTableWithJson(); *profile._didStatsTable = *pEntity->m_Qualities.m_DIDStats; /* profile._didStatsTable->remove(SETUP_DID); profile._didStatsTable->remove(MOTION_TABLE_DID); profile._didStatsTable->remove(COMBAT_TABLE_DID); profile._didStatsTable->remove(SOUND_TABLE_DID); profile._didStatsTable->remove(PALETTE_BASE_DID); profile._didStatsTable->remove(CLOTHINGBASE_DID); profile._didStatsTable->remove(ICON_DID); profile._didStatsTable->remove(PHYSICS_EFFECT_TABLE_DID); profile._didStatsTable->remove(DEATH_TREASURE_TYPE_DID); profile._didStatsTable->remove(TSYS_MUTATION_FILTER_DID); profile._didStatsTable->remove(MUTATE_FILTER_DID); */ for (PackableHashTable::iterator i = profile._didStatsTable->begin(); i != profile._didStatsTable->end(); ) { switch (i->first) { case EYES_PALETTE_DID: case EYES_TEXTURE_DID: case HAIR_PALETTE_DID: case NOSE_TEXTURE_DID: case SKIN_PALETTE_DID: case MOUTH_TEXTURE_DID: case ITEM_SKILL_LIMIT_DID: case SPELL_DID: case SPELL_COMPONENT_DID: case DEATH_SPELL_DID: i++; continue; } i = profile._didStatsTable->erase(i); } } if (!pEntity->IsCreature() && pEntity->m_Qualities._spell_book) { profile._spellBook = new SmartArray(); profile._spellBook->grow((DWORD)pEntity->m_Qualities._spell_book->_spellbook.size()); for (auto &spell : pEntity->m_Qualities._spell_book->_spellbook) profile._spellBook->add((DWORD *)&spell.first); } if (pEntity->IsCreature()) { profile.creature_profile = new CreatureAppraisalProfile(); auto setCreatureProfileAttribute = [&](STypeAttribute attrib, DWORD &value, DWORD &bitfield) { DWORD raw = 0; pEntity->m_Qualities.InqAttribute(attrib, raw, TRUE); pEntity->m_Qualities.InqAttribute(attrib, value, FALSE); if (raw != value) { switch (attrib) { case STRENGTH_ATTRIBUTE: bitfield |= CreatureAppraisalProfile::BF_STRENGTH; break; case ENDURANCE_ATTRIBUTE: bitfield |= CreatureAppraisalProfile::BF_ENDURANCE; break; case QUICKNESS_ATTRIBUTE: bitfield |= CreatureAppraisalProfile::BF_QUICKNESS; break; case COORDINATION_ATTRIBUTE: bitfield |= CreatureAppraisalProfile::BF_COORDINATION; break; case FOCUS_ATTRIBUTE: bitfield |= CreatureAppraisalProfile::BF_FOCUS; break; case SELF_ATTRIBUTE: bitfield |= CreatureAppraisalProfile::BF_SELF; break; } if (value > raw) { switch (attrib) { case STRENGTH_ATTRIBUTE: bitfield |= CreatureAppraisalProfile::BF_STRENGTH_HI; break; case ENDURANCE_ATTRIBUTE: bitfield |= CreatureAppraisalProfile::BF_ENDURANCE_HI; break; case QUICKNESS_ATTRIBUTE: bitfield |= CreatureAppraisalProfile::BF_QUICKNESS_HI; break; case COORDINATION_ATTRIBUTE: bitfield |= CreatureAppraisalProfile::BF_COORDINATION_HI; break; case FOCUS_ATTRIBUTE: bitfield |= CreatureAppraisalProfile::BF_FOCUS_HI; break; case SELF_ATTRIBUTE: bitfield |= CreatureAppraisalProfile::BF_SELF_HI; break; } } } }; auto setCreatureProfileAttribute2nd = [&](STypeAttribute2nd attrib2nd, DWORD &value, DWORD &bitfield) { DWORD raw = 0; pEntity->m_Qualities.InqAttribute2nd(attrib2nd, raw, TRUE); pEntity->m_Qualities.InqAttribute2nd(attrib2nd, value, FALSE); if (raw != value) { switch (attrib2nd) { case MAX_HEALTH_ATTRIBUTE_2ND: bitfield |= CreatureAppraisalProfile::BF_MAX_HEALTH; break; case MAX_STAMINA_ATTRIBUTE_2ND: bitfield |= CreatureAppraisalProfile::BF_MAX_STAMINA; break; case MAX_MANA_ATTRIBUTE_2ND: bitfield |= CreatureAppraisalProfile::BF_MAX_MANA; break; } if (value > raw) { switch (attrib2nd) { case MAX_HEALTH_ATTRIBUTE_2ND: bitfield |= CreatureAppraisalProfile::BF_MAX_HEALTH_HI; break; case MAX_STAMINA_ATTRIBUTE_2ND: bitfield |= CreatureAppraisalProfile::BF_MAX_STAMINA_HI; break; case MAX_MANA_ATTRIBUTE_2ND: bitfield |= CreatureAppraisalProfile::BF_MAX_MANA_HI; break; } } } }; setCreatureProfileAttribute(STRENGTH_ATTRIBUTE, profile.creature_profile->strength, profile.creature_profile->enchantment_bitfield); setCreatureProfileAttribute(ENDURANCE_ATTRIBUTE, profile.creature_profile->endurance, profile.creature_profile->enchantment_bitfield); setCreatureProfileAttribute(QUICKNESS_ATTRIBUTE, profile.creature_profile->quickness, profile.creature_profile->enchantment_bitfield); setCreatureProfileAttribute(COORDINATION_ATTRIBUTE, profile.creature_profile->coordination, profile.creature_profile->enchantment_bitfield); setCreatureProfileAttribute(FOCUS_ATTRIBUTE, profile.creature_profile->focus, profile.creature_profile->enchantment_bitfield); setCreatureProfileAttribute(SELF_ATTRIBUTE, profile.creature_profile->self, profile.creature_profile->enchantment_bitfield); setCreatureProfileAttribute2nd(HEALTH_ATTRIBUTE_2ND, profile.creature_profile->health, profile.creature_profile->enchantment_bitfield); setCreatureProfileAttribute2nd(MAX_HEALTH_ATTRIBUTE_2ND, profile.creature_profile->max_health, profile.creature_profile->enchantment_bitfield); setCreatureProfileAttribute2nd(STAMINA_ATTRIBUTE_2ND, profile.creature_profile->stamina, profile.creature_profile->enchantment_bitfield); setCreatureProfileAttribute2nd(MAX_STAMINA_ATTRIBUTE_2ND, profile.creature_profile->max_stamina, profile.creature_profile->enchantment_bitfield); setCreatureProfileAttribute2nd(MANA_ATTRIBUTE_2ND, profile.creature_profile->mana, profile.creature_profile->enchantment_bitfield); setCreatureProfileAttribute2nd(MAX_MANA_ATTRIBUTE_2ND, profile.creature_profile->max_mana, profile.creature_profile->enchantment_bitfield); } if (pEntity->InqType() & TYPE_WEAPON) { profile.weapon_profile = new WeaponProfile(); auto setCreatureProfileAttribute2nd = [&](STypeAttribute2nd attrib2nd, DWORD &value, DWORD &bitfield) { DWORD raw = 0; pEntity->m_Qualities.InqAttribute2nd(attrib2nd, raw, TRUE); pEntity->m_Qualities.InqAttribute2nd(attrib2nd, value, FALSE); if (raw != value) { switch (attrib2nd) { case MAX_HEALTH_ATTRIBUTE_2ND: bitfield |= CreatureAppraisalProfile::BF_MAX_HEALTH; break; case MAX_STAMINA_ATTRIBUTE_2ND: bitfield |= CreatureAppraisalProfile::BF_MAX_STAMINA; break; case MAX_MANA_ATTRIBUTE_2ND: bitfield |= CreatureAppraisalProfile::BF_MAX_MANA; break; } if (value > raw) { switch (attrib2nd) { case MAX_HEALTH_ATTRIBUTE_2ND: bitfield |= CreatureAppraisalProfile::BF_MAX_HEALTH_HI; break; case MAX_STAMINA_ATTRIBUTE_2ND: bitfield |= CreatureAppraisalProfile::BF_MAX_STAMINA_HI; break; case MAX_MANA_ATTRIBUTE_2ND: bitfield |= CreatureAppraisalProfile::BF_MAX_MANA_HI; break; } } } }; profile.weapon_profile->damage_type = (DAMAGE_TYPE)pEntity->InqIntQuality(DAMAGE_TYPE_INT, 0); int baseWeaponTime = max(0, min(200, pEntity->InqIntQuality(WEAPON_TIME_INT, 0, TRUE))); profile.weapon_profile->weapon_time = max(0, min(200, pEntity->GetAttackTime())); if (baseWeaponTime != profile.weapon_profile->weapon_time) { profile.weapon_ench_bitfield |= WeaponEnchantment_BFIndex::BF_WEAPON_TIME; if (profile.weapon_profile->weapon_time < baseWeaponTime) profile.weapon_ench_bitfield |= WeaponEnchantment_BFIndex::BF_WEAPON_TIME_HI; } profile.weapon_profile->weapon_skill = pEntity->InqIntQuality(WEAPON_SKILL_INT, 0); if (!pEntity->AsCaster()) { int baseWeaponDamage = pEntity->InqIntQuality(DAMAGE_INT, 0, TRUE); profile.weapon_profile->weapon_damage = pEntity->GetAttackDamage(); if (baseWeaponDamage != profile.weapon_profile->weapon_damage) { profile.weapon_ench_bitfield |= WeaponEnchantment_BFIndex::BF_DAMAGE; if (profile.weapon_profile->weapon_damage > baseWeaponDamage) profile.weapon_ench_bitfield |= WeaponEnchantment_BFIndex::BF_DAMAGE_HI; } profile.weapon_profile->damage_variance = pEntity->InqFloatQuality(DAMAGE_VARIANCE_FLOAT, 0.0); profile.weapon_profile->damage_mod = pEntity->InqFloatQuality(DAMAGE_MOD_FLOAT, 1.0); profile.weapon_profile->weapon_length = pEntity->InqFloatQuality(WEAPON_LENGTH_FLOAT, 0); double baseWeaponOffense = pEntity->InqFloatQuality(WEAPON_OFFENSE_FLOAT, 0, TRUE); profile.weapon_profile->weapon_offense = pEntity->GetOffenseMod(); if (fabs(baseWeaponOffense - profile.weapon_profile->weapon_offense) >= F_EPSILON) { profile.weapon_ench_bitfield |= WeaponEnchantment_BFIndex::BF_WEAPON_OFFENSE; if (profile.weapon_profile->weapon_offense > baseWeaponOffense) profile.weapon_ench_bitfield |= WeaponEnchantment_BFIndex::BF_WEAPON_OFFENSE_HI; } profile.weapon_profile->max_velocity = pEntity->InqFloatQuality(MAXIMUM_VELOCITY_FLOAT, 0, TRUE); profile.weapon_profile->max_velocity_estimated = (int)0; } else { profile.weapon_profile->weapon_damage = 0; profile.weapon_profile->damage_variance = 0; } } if (pEntity->InqType() & TYPE_VESTEMENTS) { auto setArmorProfileMod = [&](STypeFloat floatStat, float &value, DWORD &bitfield, float &mod) { double raw = 0.0; double dbl_value = 0.0; pEntity->m_Qualities.InqFloat(floatStat, raw, TRUE); pEntity->m_Qualities.InqFloat(floatStat, dbl_value, FALSE); value = (float)dbl_value * mod; if (abs(raw - dbl_value) > F_EPSILON) { switch (floatStat) { case ARMOR_MOD_VS_SLASH_FLOAT: bitfield |= BF_ARMOR_MOD_VS_SLASH; break; case ARMOR_MOD_VS_PIERCE_FLOAT: bitfield |= BF_ARMOR_MOD_VS_PIERCE; break; case ARMOR_MOD_VS_BLUDGEON_FLOAT: bitfield |= BF_ARMOR_MOD_VS_BLUDGEON; break; case ARMOR_MOD_VS_COLD_FLOAT: bitfield |= BF_ARMOR_MOD_VS_COLD; break; case ARMOR_MOD_VS_FIRE_FLOAT: bitfield |= BF_ARMOR_MOD_VS_FIRE; break; case ARMOR_MOD_VS_ACID_FLOAT: bitfield |= BF_ARMOR_MOD_VS_ACID; break; case ARMOR_MOD_VS_ELECTRIC_FLOAT: bitfield |= BF_ARMOR_MOD_VS_ELECTRIC; break; case ARMOR_MOD_VS_NETHER_FLOAT: bitfield |= BF_ARMOR_MOD_VS_NETHER; break; } if (dbl_value > raw) { switch (floatStat) { case ARMOR_MOD_VS_SLASH_FLOAT: bitfield |= BF_ARMOR_MOD_VS_SLASH_HI; break; case ARMOR_MOD_VS_PIERCE_FLOAT: bitfield |= BF_ARMOR_MOD_VS_PIERCE_HI; break; case ARMOR_MOD_VS_BLUDGEON_FLOAT: bitfield |= BF_ARMOR_MOD_VS_BLUDGEON_HI; break; case ARMOR_MOD_VS_COLD_FLOAT: bitfield |= BF_ARMOR_MOD_VS_COLD_HI; break; case ARMOR_MOD_VS_FIRE_FLOAT: bitfield |= BF_ARMOR_MOD_VS_FIRE_HI; break; case ARMOR_MOD_VS_ACID_FLOAT: bitfield |= BF_ARMOR_MOD_VS_ACID_HI; break; case ARMOR_MOD_VS_ELECTRIC_FLOAT: bitfield |= BF_ARMOR_MOD_VS_ELECTRIC_HI; break; case ARMOR_MOD_VS_NETHER_FLOAT: bitfield |= BF_ARMOR_MOD_VS_NETHER_HI; break; } } } }; int* shieldLoc = profile._intStatsTable->lookup(LOCATIONS_INT); float shieldMod = 1; if (shieldLoc && *shieldLoc == static_cast(INVENTORY_LOC::SHIELD_LOC)) { SKILL_ADVANCEMENT_CLASS sac = SKILL_ADVANCEMENT_CLASS::UNTRAINED_SKILL_ADVANCEMENT_CLASS; pSource->m_Qualities.InqSkillAdvancementClass(SHIELD_SKILL, sac); if (sac != SPECIALIZED_SKILL_ADVANCEMENT_CLASS) shieldMod = .5; } profile.armor_profile = new ArmorProfile(); setArmorProfileMod(ARMOR_MOD_VS_SLASH_FLOAT, profile.armor_profile->mod_vs_slash, profile.armor_ench_bitfield, shieldMod); setArmorProfileMod(ARMOR_MOD_VS_PIERCE_FLOAT, profile.armor_profile->mod_vs_pierce, profile.armor_ench_bitfield, shieldMod); setArmorProfileMod(ARMOR_MOD_VS_BLUDGEON_FLOAT, profile.armor_profile->mod_vs_bludgeon, profile.armor_ench_bitfield, shieldMod); setArmorProfileMod(ARMOR_MOD_VS_COLD_FLOAT, profile.armor_profile->mod_vs_cold, profile.armor_ench_bitfield, shieldMod); setArmorProfileMod(ARMOR_MOD_VS_FIRE_FLOAT, profile.armor_profile->mod_vs_fire, profile.armor_ench_bitfield, shieldMod); setArmorProfileMod(ARMOR_MOD_VS_ACID_FLOAT, profile.armor_profile->mod_vs_acid, profile.armor_ench_bitfield, shieldMod); setArmorProfileMod(ARMOR_MOD_VS_ELECTRIC_FLOAT, profile.armor_profile->mod_vs_electric, profile.armor_ench_bitfield, shieldMod); setArmorProfileMod(ARMOR_MOD_VS_NETHER_FLOAT, profile.armor_profile->mod_vs_nether, profile.armor_ench_bitfield, shieldMod); int *enchanted_armor_level = NULL; if (profile._intStatsTable && (enchanted_armor_level = profile._intStatsTable->lookup(ARMOR_LEVEL_INT))) { int raw_armor_level = pEntity->InqIntQuality(ARMOR_LEVEL_INT, 0, TRUE); if (raw_armor_level != *enchanted_armor_level) { profile.armor_ench_bitfield |= BF_ARMOR_LEVEL; if (*enchanted_armor_level > raw_armor_level) profile.armor_ench_bitfield |= BF_ARMOR_LEVEL_HI; } } } pWriter->Write(&profile); return pWriter; } BinaryWriter *LoginCharacter(std::shared_ptr pPlayer) { BinaryWriter *LC = new BinaryWriter; LC->Write(0x13); CACQualities loginQualities; loginQualities.CopyFrom(&pPlayer->m_Qualities); SafeDelete(loginQualities._body); SafeDelete(loginQualities._generator_table); SafeDelete(loginQualities._generator_registry); SafeDelete(loginQualities._generator_queue); SafeDelete(loginQualities._emote_table); SafeDelete(loginQualities._create_list); SafeDelete(loginQualities._event_filter); LC->Write(&loginQualities); PlayerModule module = pPlayer->_playerModule; module.Pack(LC); /* // Player Module LC->Write(0x0460); // flags for what is after this LC->Write(pPlayer->m_CharacterOptions); // options LC->Write(0x00000000);//spellbar 1 count LC->Write(0x00000000);//spellbar 2 count LC->Write(0x00000000);//spellbar 3 count LC->Write(0x00000000);//spellbar 4 count LC->Write(0x00000000);//spellbar 5 count LC->Write(0x00000000);//spellbar 6 count LC->Write(0x00000000);//spellbar 7 count LC->Write(0x00000000);//spellbar 8 count LC->Write(0x00003FFF); // more stuff LC->Write(pPlayer->m_CharacterOptions2); // more stuff LC->Align(); */ PackableList inventoryList; for (auto item : pPlayer->m_Items) { std::shared_ptr pItem = item.lock(); if (!pItem) { continue; } ContentProfile prof; prof.m_iid = pItem->GetID(); prof.m_uContainerProperties = 0; inventoryList.push_back(prof); } for (auto item : pPlayer->m_Packs) { std::shared_ptr pItem = item.lock(); if (!pItem) { continue; } ContentProfile prof; prof.m_iid = pItem->GetID(); prof.m_uContainerProperties = pItem->AsContainer() ? 1 : 2; inventoryList.push_back(prof); } inventoryList.Pack(LC); PackableList wieldedList; for (auto wielded : pPlayer->m_Wielded) { std::shared_ptr pItem = wielded.lock(); if (!pItem) { continue; } InventoryPlacement place; place.iid_ = pItem->GetID(); place.loc_ = pItem->InqIntQuality(CURRENT_WIELDED_LOCATION_INT, 0); place.priority_ = pItem->InqIntQuality(CLOTHING_PRIORITY_INT, 0); wieldedList.push_back(place); } wieldedList.Pack(LC); return LC; } BinaryWriter *HealthUpdate(std::shared_ptr pWeenie) { MESSAGE_BEGIN(HealthUpdate); HealthUpdate->Write(0x1C0); HealthUpdate->Write(pWeenie->GetID()); DWORD health = 0, maxHealth = 1; pWeenie->m_Qualities.InqAttribute2nd(HEALTH_ATTRIBUTE_2ND, health, FALSE); pWeenie->m_Qualities.InqAttribute2nd(MAX_HEALTH_ATTRIBUTE_2ND, maxHealth, FALSE); double fraction = min(1.0, max(0.0, maxHealth != 0 ? health / (double)maxHealth : 0.0)); HealthUpdate->Write((float)fraction); MESSAGE_END(HealthUpdate); } BinaryWriter *ItemManaUpdate(std::shared_ptr item) { MESSAGE_BEGIN(ItemManaUpdate); int currentMana = item->InqIntQuality(ITEM_CUR_MANA_INT, -1, TRUE); int maxMana = item->InqIntQuality(ITEM_MAX_MANA_INT, -1, TRUE); DWORD hasMana; float manaFraction; if (maxMana < 0 || currentMana < 0) { hasMana = 0; manaFraction = 0; } else { hasMana = 1; manaFraction = (float)currentMana / (float)maxMana; } manaFraction = min(max(manaFraction, 0), 1); ItemManaUpdate->Write(0x0264); ItemManaUpdate->Write(item->GetID()); ItemManaUpdate->Write(manaFraction); ItemManaUpdate->Write(hasMana); MESSAGE_END(ItemManaUpdate); } BinaryWriter *InventoryEquip(DWORD dwItemID, DWORD dwCoverage) { MESSAGE_BEGIN(InventoryEquip); InventoryEquip->Write(0x23); InventoryEquip->Write(dwItemID); InventoryEquip->Write(dwCoverage); MESSAGE_END(InventoryEquip); } BinaryWriter *InventoryMove(DWORD dwItemID, DWORD dwContainerID, DWORD dwSlot, DWORD dwType) { MESSAGE_BEGIN(InventoryMove); InventoryMove->Write(0x22); InventoryMove->Write(dwItemID); InventoryMove->Write(dwContainerID); InventoryMove->Write(dwSlot); InventoryMove->Write(dwType); MESSAGE_END(InventoryMove); } BinaryWriter *InventoryDrop(DWORD dwItemID) { MESSAGE_BEGIN(InventoryDrop); InventoryDrop->Write(0x19A); InventoryDrop->Write(dwItemID); MESSAGE_END(InventoryDrop); } BinaryWriter *MoveUpdate(std::shared_ptr pEntity) { BinaryWriter *pWriter = new BinaryWriter(); pWriter->Write(0xF748); pWriter->Write(pEntity->GetID()); PositionPack position; position.position = pEntity->m_Position; position.has_contact = (pEntity->transient_state & CONTACT_TS && pEntity->transient_state & ON_WALKABLE_TS) ? TRUE : FALSE; position.placement_id = pEntity->GetActivePlacementFrameID(); position.instance_timestamp = pEntity->_instance_timestamp; position.position_timestamp = pEntity->_position_timestamp; position.teleport_timestamp = pEntity->_teleport_timestamp; // position.velocity = pEntity->m_velocityVector; position.force_position_timestamp = pEntity->_force_position_timestamp; position.Pack(pWriter); return pWriter; }