#include #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(CWeenieObject *pEntity) { BinaryWriter *pWriter = new BinaryWriter; PublicWeenieDesc *pDesc = PublicWeenieDesc::CreateFromWeenie(pEntity); pDesc->Pack(pWriter); delete pDesc; return pWriter; #if 0 BinaryWriter OptionalWeenieObjData; uint32_t 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; uint32_t dwCoverage1 = pItem->GetCoverage1(); uint32_t dwCoverage2 = pItem->GetCoverage2(); uint32_t 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->WritePackeduint32_t(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(CWeenieObject *pEntity) { BinaryWriter OptionalPhysicsObjData; uint32_t 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); } uint32_t motion_table = 0; if (pEntity->m_Qualities.InqDataID(MOTION_TABLE_DID, motion_table) && motion_table) { dwSections |= PhysicsDescInfo::MTABLE; OptionalPhysicsObjData.Write(motion_table); } uint32_t sound_table = 0; if (pEntity->m_Qualities.InqDataID(SOUND_TABLE_DID, sound_table) && sound_table) { dwSections |= PhysicsDescInfo::STABLE; OptionalPhysicsObjData.Write(sound_table); } uint32_t pe_table = 0; if (pEntity->m_Qualities.InqDataID(PHYSICS_EFFECT_TABLE_DID, pe_table) && pe_table) { dwSections |= PhysicsDescInfo::PETABLE; OptionalPhysicsObjData.Write(pe_table); } uint32_t setup_id = 0; if (pEntity->m_Qualities.InqDataID(SETUP_DID, setup_id) && setup_id) { dwSections |= PhysicsDescInfo::CSETUP; OptionalPhysicsObjData.Write(setup_id); } if (pEntity->parent) { dwSections |= PhysicsDescInfo::PARENT; OptionalPhysicsObjData.Write(pEntity->parent->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 (uint32_t i = 0; i < pEntity->children->num_objects; i++) { OptionalPhysicsObjData.Write(pEntity->children->objects.array_data[i]->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(CWeenieObject *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(CWeenieObject *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(CWeenieObject *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, uint32_t &value, uint32_t &bitfield) { uint32_t 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(CWeenieObject *pSource, CWeenieObject *pEntity, uint32_t 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; uint32_t characterOptions; uint32_t characterOptions2; if (CPlayerWeenie *pPlayerSource = pSource->AsPlayer()) { bIsSourceAdmin = pPlayerSource->GetAccessLevel() >= ADMIN_ACCESS; } if (CPlayerWeenie *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(ITEM_TYPE_INT); profile._intStatsTable->remove(MASS_INT); profile._intStatsTable->remove(ITEM_USEABLE_INT); profile._intStatsTable->remove(STACK_UNIT_MASS_INT); profile._intStatsTable->remove(ARMOR_TYPE_INT); profile._intStatsTable->remove(TSYS_MUTATION_DATA_INT); profile._intStatsTable->remove(RESIST_ITEM_APPRAISAL_INT); profile._intStatsTable->remove(GENERATOR_TYPE_INT); profile._intStatsTable->remove(GENERATOR_DESTRUCTION_TYPE_INT); profile._intStatsTable->remove(GENERATOR_TIME_TYPE_INT); profile._intStatsTable->remove(GENERATOR_START_TIME_INT); profile._intStatsTable->remove(GENERATOR_END_TIME_INT); profile._intStatsTable->remove(GENERATOR_END_DESTRUCTION_TYPE_INT); profile._intStatsTable->remove(INIT_GENERATED_OBJECTS_INT); profile._intStatsTable->remove(ACTIVATION_CREATE_CLASS_INT); profile._intStatsTable->remove(ACTIVATION_RESPONSE_INT); profile._intStatsTable->remove(ACTIVE_INT); profile._intStatsTable->remove(VENDOR_HAPPY_MEAN_INT); profile._intStatsTable->remove(VENDOR_HAPPY_VARIANCE_INT); profile._intStatsTable->remove(VENDOR_HAPPY_MAX_ITEMS_INT); profile._intStatsTable->remove(AI_OPTIONS_INT); profile._intStatsTable->remove(TOLERANCE_INT); profile._intStatsTable->remove(TARGETING_TACTIC_INT); profile._intStatsTable->remove(AI_ALLOWED_COMBAT_STYLE_INT); profile._intStatsTable->remove(COMBAT_MODE_INT); profile._intStatsTable->remove(FRIEND_TYPE_INT); profile._intStatsTable->remove(FOE_TYPE_INT); profile._intStatsTable->remove(HOUSE_MAX_HOOKS_USABLE_INT); profile._intStatsTable->remove(HOUSE_CURRENT_HOOKS_USABLE_INT); profile._intStatsTable->remove(HOUSE_TYPE_INT); profile._intStatsTable->remove(PK_LEVEL_MODIFIER_INT); if (pEntity->m_Qualities.GetInt(ITEM_TYPE_INT, 0) == ITEM_TYPE::TYPE_TINKERING_MATERIAL) { profile._intStatsTable->remove(STRUCTURE_INT); profile._intStatsTable->remove(MAX_STRUCTURE_INT); } 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); int *armorLevel = profile._intStatsTable->lookup(ARMOR_LEVEL_INT); if (armorLevel) { if (sac == SPECIALIZED_SKILL_ADVANCEMENT_CLASS) { profile._intStatsTable->add(SHIELD_VALUE_INT, armorLevel); } else { int *halfShieldValue = armorLevel; int halfTrueValue = *halfShieldValue; halfTrueValue /= 2; profile._intStatsTable->add(SHIELD_VALUE_INT, &halfTrueValue); } } } if (pEntity->m_Qualities.GetInt(LIFESPAN_INT, 0) && pEntity->_timeToRot) { int newLifespan = (int)(pEntity->_timeToRot - Timer::cur_time); profile._intStatsTable->add(REMAINING_LIFESPAN_INT, &newLifespan); // Update the remaining_lifespan_int for inspect window. } if (pEntity->InqBoolQuality(LOCKED_BOOL, 0)) { Skill skill; uint32_t lockpickSkill = 0; pSource->m_Qualities.InqSkill(LOCKPICK_SKILL, skill); lockpickSkill = pSource->m_Qualities.InqSkillLevel(LOCKPICK_SKILL, lockpickSkill); if (skill._sac >= TRAINED_SKILL_ADVANCEMENT_CLASS) { int success = GetSkillChance(lockpickSkill, pEntity->InqIntQuality(RESIST_LOCKPICK_INT, 0)); profile._intStatsTable->add(APPRAISAL_LOCKPICK_SUCCESS_PERCENT_INT, &success); // Update the lockpick success chance for inspect window. } } } 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); profile._boolStatsTable->remove(CORPSE_GENERATED_RARE_BOOL); profile._boolStatsTable->remove(RESET_MESSAGE_PENDING_BOOL); profile._boolStatsTable->remove(DEFAULT_LOCKED_BOOL); profile._boolStatsTable->remove(DEFAULT_OPEN_BOOL); profile._boolStatsTable->remove(AI_USES_MANA_BOOL); profile._boolStatsTable->remove(NEVER_FAIL_CASTING_BOOL); profile._boolStatsTable->remove(VENDOR_SERVICE_BOOL); profile._boolStatsTable->remove(AI_IMMOBILE_BOOL); profile._boolStatsTable->remove(IGNORE_MAGIC_RESIST_BOOL); profile._boolStatsTable->remove(IGNORE_MAGIC_ARMOR_BOOL); profile._boolStatsTable->remove(DONT_TURN_OR_MOVE_WHEN_GIVING_BOOL); profile._boolStatsTable->remove(NPC_LOOKS_LIKE_OBJECT_BOOL); profile._boolStatsTable->remove(NO_CORPSE_BOOL); profile._boolStatsTable->remove(AI_IMMOBILE_BOOL); profile._boolStatsTable->remove(ALLOW_GIVE_BOOL); profile._boolStatsTable->remove(ALLOW_EDGE_SLIDE_BOOL); profile._boolStatsTable->remove(IS_DYNAMIC_BOOL); profile._boolStatsTable->remove(NODRAW_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); profile._floatStatsTable->remove(CREATION_TIMESTAMP_FLOAT); profile._floatStatsTable->remove(START_TIME_FLOAT); profile._floatStatsTable->remove(STOP_TIME_FLOAT); profile._floatStatsTable->remove(GENERATOR_RADIUS_FLOAT); profile._floatStatsTable->remove(GENERATOR_INITIAL_DELAY_FLOAT); profile._floatStatsTable->remove(LOCKPICK_MOD_FLOAT); profile._floatStatsTable->remove(BULK_MOD_FLOAT); profile._floatStatsTable->remove(SIZE_MOD_FLOAT); profile._floatStatsTable->remove(MINIMUM_TIME_SINCE_PK_FLOAT); double *weapon_defense = profile._floatStatsTable->lookup(WEAPON_DEFENSE_FLOAT); double old_weapon_defense = weapon_defense ? *weapon_defense : 1.0; double *mana_con = profile._floatStatsTable->lookup(MANA_CONVERSION_MOD_FLOAT); double old_mana_con = mana_con ? *mana_con : 0.0; double *elemental_dmg = profile._floatStatsTable->lookup(ELEMENTAL_DAMAGE_MOD_FLOAT); double old_elemental_dmg = elemental_dmg ? *elemental_dmg : 0.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(); // Don't enchant Ammunition if ((fabs(*weapon_defense - old_weapon_defense) >= F_EPSILON) && pEntity->m_Qualities.m_WeenieType != Ammunition_WeenieType) { profile.weapon_ench_bitfield |= BF_WEAPON_DEFENSE; if (*weapon_defense > old_weapon_defense) profile.weapon_ench_bitfield |= BF_WEAPON_DEFENSE_HI; } } if (mana_con) { *mana_con = pEntity->GetManaCon(); if (fabs(*mana_con - old_mana_con) >= F_EPSILON) { profile.resist_ench_bitfield |= BF_MANA_CON_MOD; if (*mana_con > old_mana_con) profile.resist_ench_bitfield |= BF_MANA_CON_MOD_HI; } } if (elemental_dmg) { *elemental_dmg = pEntity->GetElementalDamage(); if (fabs(*elemental_dmg - old_elemental_dmg) >= F_EPSILON) { profile.resist_ench_bitfield |= BF_ELE_DAMAGE_MOD; if (*elemental_dmg > old_elemental_dmg) profile.resist_ench_bitfield |= BF_ELE_DAMAGE_MOD_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); } if (pEntity->m_Qualities.GetBool(CORPSE_GENERATED_RARE_BOOL, 0)) { std::string longDesc = pEntity->m_Qualities.GetString(LONG_DESC_STRING, ""); if (longDesc.find(" This corpse generated a rare item!") == std::string::npos) { longDesc = longDesc + " This corpse generated a rare item!"; pEntity->m_Qualities.SetString(LONG_DESC_STRING, longDesc); } } 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); profile._strStatsTable->remove(NAME_STRING); if (pEntity->InqBoolQuality(PORTAL_SHOW_DESTINATION_BOOL, 0)) { string dest = pEntity->InqStringQuality(APPRAISAL_PORTAL_DESTINATION_STRING, ""); profile._strStatsTable->add(APPRAISAL_PORTAL_DESTINATION_STRING, &dest); } 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); */ profile._didStatsTable->remove(COMBAT_TABLE_DID); profile._didStatsTable->remove(TSYS_MUTATION_FILTER_DID); profile._didStatsTable->remove(MUTATE_FILTER_DID); profile._didStatsTable->remove(DEATH_TREASURE_TYPE_DID); profile._didStatsTable->remove(USE_TARGET_ANIMATION_DID); profile._didStatsTable->remove(USE_TARGET_SUCCESS_ANIMATION_DID); profile._didStatsTable->remove(USE_TARGET_FAILURE_ANIMATION_DID); profile._didStatsTable->remove(USE_USER_ANIMATION_DID); profile._didStatsTable->remove(LINKED_PORTAL_ONE_DID); profile._didStatsTable->remove(LINKED_PORTAL_TWO_DID); profile._didStatsTable->remove(WIELDED_TREASURE_TYPE_DID); profile._didStatsTable->remove(INVENTORY_TREASURE_TYPE_DID); profile._didStatsTable->remove(SHOP_TREASURE_TYPE_DID); profile._didStatsTable->remove(HOUSEID_DID); profile._didStatsTable->remove(RESTRICTION_EFFECT_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: case PROC_SPELL_DID: i++; continue; } i = profile._didStatsTable->erase(i); } } uint32_t spelldid = 0; if (!pEntity->IsCreature() && pEntity->m_Qualities._spell_book) { profile._spellBook = new SmartArray(); profile._spellBook->grow((uint32_t)pEntity->m_Qualities._spell_book->_spellbook.size()); for (auto &spell : pEntity->m_Qualities._spell_book->_spellbook) profile._spellBook->add((uint32_t *)&spell.first); // Has spellbook, check if spell_did exists, if not, add. if (pEntity->m_Qualities.InqDataID(SPELL_DID, spelldid)) { int i = 0; while (i < profile._spellBook->num_used) { if (profile._spellBook->array_data[i] == spelldid) break; i++; } if (i == profile._spellBook->num_used) profile._spellBook->add(&spelldid); } } else if (pEntity->m_Qualities.InqDataID(SPELL_DID, spelldid) && !pEntity->AsScroll()) { // Does not have spellbook, add spell_did profile._spellBook = new SmartArray(); profile._spellBook->grow(spelldid); profile._spellBook->add(&spelldid); } if (pEntity->IsCreature() && !pEntity->m_Qualities.GetBool(NPC_LOOKS_LIKE_OBJECT_BOOL, false)) { profile.creature_profile = new CreatureAppraisalProfile(); auto setCreatureProfileAttribute = [&](STypeAttribute attrib, uint32_t &value, uint32_t &bitfield) { uint32_t 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, uint32_t &value, uint32_t &bitfield) { uint32_t 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, uint32_t &value, uint32_t &bitfield) { uint32_t 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->m_Qualities.m_WeenieType == Ammunition_WeenieType ? baseWeaponDamage : pEntity->GetAttackDamage(); // Don't enchant Ammunition if ((baseWeaponDamage != profile.weapon_profile->weapon_damage) && pEntity->m_Qualities.m_WeenieType != Ammunition_WeenieType) { 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(); // Don't enchant Ammunition if ((fabs(baseWeaponOffense - profile.weapon_profile->weapon_offense) >= F_EPSILON) && pEntity->m_Qualities.m_WeenieType != Ammunition_WeenieType) { 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, uint32_t &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; } } } if (pEntity->_IsPlayer()) { int coverage = 0; int armorLevel = 0; for (auto wielded : pEntity->AsPlayer()->m_Wielded) { coverage = wielded->m_Qualities.GetInt(CLOTHING_PRIORITY_INT, 0); armorLevel = wielded->m_Qualities.GetInt(ARMOR_LEVEL_INT, 0); if (armorLevel > 0) { if (coverage & CLOTHING_PRIORITY::HEAD_WEAR_CLOTHING_PRIORITY) { profile.base_armor_head += armorLevel; } if (coverage & CLOTHING_PRIORITY::HAND_WEAR_CLOTHING_PRIORITY) { profile.base_armor_hand += armorLevel; } if (coverage & CLOTHING_PRIORITY::FOOT_WEAR_CLOTHING_PRIORITY) { profile.base_armor_foot += armorLevel; } if (coverage & CLOTHING_PRIORITY::CHEST_ARMOR_CLOTHING_PRIORITY || coverage & CLOTHING_PRIORITY::CHEST_WEAR_CLOTHING_PRIORITY) { profile.base_armor_chest += armorLevel; } if (coverage & CLOTHING_PRIORITY::ABDOMEN_ARMOR_CLOTHING_PRIORITY || coverage & CLOTHING_PRIORITY::ABDOMEN_WEAR_CLOTHING_PRIORITY) { profile.base_armor_groin += armorLevel; } if (coverage & CLOTHING_PRIORITY::UPPER_ARM_ARMOR_CLOTHING_PRIORITY || coverage & CLOTHING_PRIORITY::UPPER_ARM_WEAR_CLOTHING_PRIORITY) { profile.base_armor_bicep += armorLevel; } if (coverage & CLOTHING_PRIORITY::LOWER_ARM_ARMOR_CLOTHING_PRIORITY || coverage & CLOTHING_PRIORITY::LOWER_ARM_WEAR_CLOTHING_PRIORITY) { profile.base_armor_wrist += armorLevel; } if (coverage & CLOTHING_PRIORITY::UPPER_LEG_ARMOR_CLOTHING_PRIORITY || coverage & CLOTHING_PRIORITY::UPPER_LEG_WEAR_CLOTHING_PRIORITY) { profile.base_armor_thigh += armorLevel; } if (coverage & CLOTHING_PRIORITY::LOWER_LEG_ARMOR_CLOTHING_PRIORITY || coverage & CLOTHING_PRIORITY::LOWER_LEG_WEAR_CLOTHING_PRIORITY) { profile.base_armor_shin += armorLevel; } } } } pWriter->Write(&profile); return pWriter; } BinaryWriter *LoginCharacter(CPlayerWeenie *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); pPlayer->_playerModule.Pack(LC); PackableList inventoryList; for (auto item : pPlayer->m_Items) { ContentProfile prof; prof.m_iid = item->GetID(); prof.m_uContainerProperties = 0; inventoryList.push_back(prof); } for (auto item : pPlayer->m_Packs) { ContentProfile prof; prof.m_iid = item->GetID(); prof.m_uContainerProperties = item->AsContainer() ? 1 : 2; inventoryList.push_back(prof); } inventoryList.Pack(LC); PackableList wieldedList; for (auto wielded : pPlayer->m_Wielded) { InventoryPlacement place; place.iid_ = wielded->GetID(); place.loc_ = wielded->InqIntQuality(CURRENT_WIELDED_LOCATION_INT, 0); place.priority_ = wielded->InqIntQuality(CLOTHING_PRIORITY_INT, 0); wieldedList.push_back(place); } wieldedList.Pack(LC); return LC; } BinaryWriter *HealthUpdate(CWeenieObject *pWeenie) { MESSAGE_BEGIN(HealthUpdate); HealthUpdate->Write(0x1C0); HealthUpdate->Write(pWeenie->GetID()); uint32_t 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(CWeenieObject *item) { MESSAGE_BEGIN(ItemManaUpdate); int currentMana = item->InqIntQuality(ITEM_CUR_MANA_INT, -1, TRUE); int maxMana = item->InqIntQuality(ITEM_MAX_MANA_INT, -1, TRUE); uint32_t 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.0f), 1.0f); ItemManaUpdate->Write(0x0264); ItemManaUpdate->Write(item->GetID()); ItemManaUpdate->Write(manaFraction); ItemManaUpdate->Write(hasMana); MESSAGE_END(ItemManaUpdate); } BinaryWriter *InventoryEquip(uint32_t dwItemID, uint32_t dwCoverage) { MESSAGE_BEGIN(InventoryEquip); InventoryEquip->Write(0x23); InventoryEquip->Write(dwItemID); InventoryEquip->Write(dwCoverage); MESSAGE_END(InventoryEquip); } BinaryWriter *InventoryMove(uint32_t dwItemID, uint32_t dwContainerID, uint32_t dwSlot, uint32_t dwType) { MESSAGE_BEGIN(InventoryMove); InventoryMove->Write(0x22); InventoryMove->Write(dwItemID); InventoryMove->Write(dwContainerID); InventoryMove->Write(dwSlot); InventoryMove->Write(dwType); MESSAGE_END(InventoryMove); } BinaryWriter *InventoryDrop(uint32_t dwItemID) { MESSAGE_BEGIN(InventoryDrop); InventoryDrop->Write(0x19A); InventoryDrop->Write(dwItemID); MESSAGE_END(InventoryDrop); } BinaryWriter *MoveUpdate(CWeenieObject *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; }