Mercurial > mm7
view Engine/Objects/Actor.cpp @ 2541:a902abdfc7f2
1. Renamed class Game to class Engine.
2. Separated game logic as state of FSM from game logic as engine.
3. Found out that many UI screen initializers were optimized away, intially
they all returned newly created window as separate object like it is done
in CharacterUI_Initialize.
author | a.parshin |
---|---|
date | Sun, 10 May 2015 01:29:11 +0200 |
parents | 1bcadc6dd203 |
children | b6140dfeac27 |
line wrap: on
line source
#define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h> #define _CRT_SECURE_NO_WARNINGS #include "Engine/Engine.h" #include "../Graphics/PaletteManager.h" #include "../Graphics/DecalBuilder.h" #include "../Graphics/Sprites.h" #include "../../stru6.h" #include "Actor.h" #include "../OurMath.h" #include "../Graphics/Outdoor.h" #include "Media/Audio/AudioPlayer.h" #include "ObjectList.h" #include "../Graphics/Overlays.h" #include "../Tables/FactionTable.h" #include "../TurnEngine/TurnEngine.h" #include "../Spells/CastSpellInfo.h" #include "../Timer.h" #include "../LOD.h" #include "../Party.h" #include "GUI/GUIWindow.h" #include "SpriteObject.h" #include "../stru298.h" #include "../Texts.h" #include "../Graphics/Level/Decoration.h" #include "../Graphics/Viewport.h" #include "../Graphics/Vis.h" std::array<Actor, 500> pActors; size_t uNumActors; stru319 stru_50C198; // idb std::array<uint, 5> _4DF380_hostilityRanges = {0, 1024, 2560, 5120, 10240}; //----- (0042FB5C) -------------------------------------------------------- // True if monster should play attack animation when casting this spell. bool ShouldMonsterPlayAttackAnim(signed int spell_id) { switch (spell_id) { case SPELL_FIRE_HASTE: case SPELL_AIR_SHIELD: case SPELL_EARTH_STONESKIN: case SPELL_SPIRIT_BLESS: case SPELL_SPIRIT_FATE: case SPELL_SPIRIT_HEROISM: case SPELL_BODY_HAMMERHANDS: case SPELL_BODY_POWER_CURE: case SPELL_LIGHT_DISPEL_MAGIC: case SPELL_LIGHT_DAY_OF_PROTECTION: case SPELL_LIGHT_HOUR_OF_POWER: case SPELL_DARK_PAIN_REFLECTION: return false; } return true; } //----- (0041AF52) -------------------------------------------------------- void Actor::DrawHealthBar(Actor *actor, GUIWindow *window) { unsigned int bar_length; // esi@1 unsigned int uX; // ebx@10 unsigned int v9; // [sp+14h] [bp-Ch]@4 unsigned int v10; // [sp+1Ch] [bp-4h]@4 if (actor->pMonsterInfo.uHP <= 25) bar_length = 25; else if ( actor->pMonsterInfo.uHP < 200 ) bar_length = actor->pMonsterInfo.uHP; else bar_length = 200; v10 = bar_length; if ( actor->sCurrentHP <= (0.34 * actor->pMonsterInfo.uHP) ) v9 = uTextureID_mhp_red; else if ( actor->sCurrentHP <= ( 0.67 * actor->pMonsterInfo.uHP) ) v9 = uTextureID_mhp_yel; else v9 = uTextureID_mhp_grn; if ( actor->sCurrentHP < (int)actor->pMonsterInfo.uHP ) v10 = bar_length / actor->pMonsterInfo.uHP * actor->sCurrentHP; uX = window->uFrameX + (signed int)(window->uFrameWidth - bar_length) / 2; pRenderer->SetUIClipRect(uX, window->uFrameY + 32, uX + bar_length, window->uFrameY + 52); pRenderer->DrawTextureIndexed(uX, window->uFrameY + 32, pIcons_LOD->GetTexture(uTextureID_mhp_bd)); pRenderer->SetUIClipRect(uX, window->uFrameY + 32, uX + v10, window->uFrameY + 52); pRenderer->DrawTextureIndexed(uX, window->uFrameY + 34, pIcons_LOD->GetTexture(v9)); pRenderer->ResetUIClipRect(); pRenderer->DrawTextureIndexed(uX - 5, window->uFrameY + 32, pIcons_LOD->GetTexture(uTextureID_mhp_capl)); pRenderer->DrawTextureIndexed(uX + bar_length, window->uFrameY + 32, pIcons_LOD->GetTexture(uTextureID_mhp_capr)); } //----- (00448A40) -------------------------------------------------------- void Actor::ToggleFlag(signed int uActorID, unsigned int uFlag, int bToggle) { if ( uActorID >= 0 && uActorID <= (signed int)(uNumActors - 1) ) { if ( bToggle ) pActors[uActorID].uAttributes |= uFlag; else { if ( uFlag == 0x10000 ) { if (pActors[uActorID].uAIState == Disabled ) pActors[uActorID].uAIState = Standing; } pActors[uActorID].uAttributes &= ~uFlag; } } } //----- (00448518) -------------------------------------------------------- void __fastcall sub_448518_npc_set_item(int npc, unsigned int item, int a3) { for (uint i = 0; i < uNumActors; i++) { if (pActors[uNumActors].sNPC_ID == npc) Actor::GiveItem(i, item, a3); } } //----- (004485A7) -------------------------------------------------------- void Actor::GiveItem(signed int uActorID, unsigned int uItemID, unsigned int bGive) { if ( (uActorID >= 0) && (signed int)uActorID <= (signed int)(uNumActors - 1) ) { if ( bGive ) { if ( pActors[uActorID].uCarriedItemID == 0) pActors[uActorID].uCarriedItemID = uItemID; else if ( pActors[uActorID].ActorHasItems[0].uItemID == 0) pActors[uActorID].ActorHasItems[0].uItemID = uItemID; else if ( pActors[uActorID].ActorHasItems[1].uItemID == 0) pActors[uActorID].ActorHasItems[1].uItemID = uItemID; } else { if ( pActors[uActorID].uCarriedItemID == uItemID ) pActors[uActorID].uCarriedItemID = 0; else if ( pActors[uActorID].ActorHasItems[0].uItemID == uItemID ) pActors[uActorID].ActorHasItems[0].Reset(); else if ( pActors[uActorID].ActorHasItems[1].uItemID == uItemID ) pActors[uActorID].ActorHasItems[1].Reset(); } } } //----- (0040894B) -------------------------------------------------------- bool Actor::CanAct() { bool isparalyzed; // esi@1 bool isstoned; // edi@2 isstoned = (signed __int64)this->pActorBuffs[ACTOR_BUFF_STONED].uExpireTime > 0;// stoned isparalyzed = (signed __int64)this->pActorBuffs[ACTOR_BUFF_PARALYZED].uExpireTime > 0;// paralyzed return !(isstoned || isparalyzed || this->uAIState == Dying || this->uAIState == Dead || this->uAIState == Removed || this->uAIState == Summoned || this->uAIState == Disabled); } //----- (004089C7) -------------------------------------------------------- bool Actor::IsNotAlive() { bool isstoned; // esi@1 isstoned = (signed __int64)this->pActorBuffs[ACTOR_BUFF_STONED].uExpireTime > 0;// stoned return (isstoned || (uAIState == Dying) || (uAIState == Dead) || (uAIState == Removed) || (uAIState == Summoned) || (uAIState == Disabled)); } //----- (004086E9) -------------------------------------------------------- void Actor::SetRandomGoldIfTheresNoItem() { int v2; // edi@1 v2 = 0; if ( !this->ActorHasItems[3].uItemID ) { if ( this->pMonsterInfo.uTreasureDiceRolls ) { for (int i = 0; i < this->pMonsterInfo.uTreasureDiceRolls; i++) v2 += rand() % this->pMonsterInfo.uTreasureDiceSides + 1; if ( v2 ) { this->ActorHasItems[3].uItemID = 197; this->ActorHasItems[3].uSpecEnchantmentType = v2; //actual gold amount } } } if ( rand() % 100 < this->pMonsterInfo.uTreasureDropChance ) { if ( this->pMonsterInfo.uTreasureLevel ) pItemsTable->GenerateItem(this->pMonsterInfo.uTreasureLevel, this->pMonsterInfo.uTreasureType, &this->ActorHasItems[2]); } this->uAttributes |= ACTOR_HAS_ITEM; } //----- (00404AC7) -------------------------------------------------------- void Actor::AI_SpellAttack(unsigned int uActorID, AIDirection *pDir, int uSpellID, int a4, unsigned int uSkillLevel) { Actor *actorPtr; // esi@1 unsigned int realPoints; // edi@1 unsigned int masteryLevel; // eax@1 int v8; // edi@16 signed int v10; // ecx@22 int v19; // edi@34 int v20; // eax@35 signed int v23; // eax@41 int v28; // st6@50 int v30; // esi@50 int v31; // ST3C_4@51 unsigned int v32; // edi@51 signed int v36; // eax@67 int v39; // ecx@75 int v42; // ecx@91 int v44; // ecx@100 int v48; // ecx@110 int v51; // ecx@130 int v54; // ecx@138 int v59; // edi@146 int v61; // edi@146 signed int v63; // edi@146 int v68; // edi@168 signed int v70; // ecx@172 int v79; // edx@185 int v80; // eax@185 signed int v91; // eax@200 int v94; // ecx@208 int v96; // ecx@217 int pitch; // [sp+2Ch] [bp-A4h]@51 int v114; // [sp+48h] [bp-88h]@41 SpriteObject a1; // [sp+4Ch] [bp-84h]@1 int v116; // [sp+BCh] [bp-14h]@49 int v118; // [sp+C4h] [bp-Ch]@29 int v119; // [sp+C8h] [bp-8h]@48 int v120; // [sp+CCh] [bp-4h]@1 int spellnuma; // [sp+D8h] [bp+8h]@29 int spellnumb; // [sp+D8h] [bp+8h]@48 int spellnumc; // [sp+D8h] [bp+8h]@50 int spellnume; // [sp+D8h] [bp+8h]@179 int a1a; // [sp+E0h] [bp+10h]@34 int a1c; // [sp+E0h] [bp+10h]@184 actorPtr = &pActors[uActorID]; realPoints = uSkillLevel & 0x3F; masteryLevel = SkillToMastery(uSkillLevel); switch (uSpellID) { case SPELL_FIRE_FIRE_BOLT: case SPELL_FIRE_FIREBALL: case SPELL_FIRE_INCINERATE: case SPELL_AIR_LIGHNING_BOLT: case SPELL_WATER_ICE_BOLT: case SPELL_WATER_ACID_BURST: case SPELL_EARTH_BLADES: case SPELL_EARTH_ROCK_BLAST: case SPELL_MIND_MIND_BLAST: case SPELL_MIND_PSYCHIC_SHOCK: case SPELL_BODY_HARM: case SPELL_LIGHT_LIGHT_BOLT: case SPELL_DARK_TOXIC_CLOUD: case SPELL_DARK_DRAGON_BREATH: a1.uType = stru_4E3ACC[uSpellID].uType; a1.uObjectDescID = GetObjDescId(uSpellID); a1.stru_24.Reset(); a1.spell_id = uSpellID; a1.spell_level = uSkillLevel; a1.vPosition.x = actorPtr->vPosition.x; a1.spell_skill = 0; a1.vPosition.y = actorPtr->vPosition.y; a1.vPosition.z = actorPtr->vPosition.z + ((signed int)actorPtr->uActorHeight >> 1); a1.uFacing = LOWORD(pDir->uYawAngle); a1.uSoundID = 0; a1.uAttributes = 0; a1.uSectorID = pIndoor->GetSector(a1.vPosition.x, a1.vPosition.y, a1.vPosition.z); a1.uSpriteFrameID = 0; a1.spell_caster_pid = PID(OBJECT_Actor, uActorID); a1.spell_target_pid = 0; if ((double)pDir->uDistance < 307.2 ) a1.field_60_distance_related_prolly_lod = 0; else if ( pDir->uDistance < 1024 ) a1.field_60_distance_related_prolly_lod = 1; else if ( pDir->uDistance < 2560 ) a1.field_60_distance_related_prolly_lod = 2; else a1.field_60_distance_related_prolly_lod = 3; a1.field_61 = 2; v91 = a1.Create(pDir->uYawAngle, pDir->uPitchAngle, pObjectList->pObjects[(signed __int16)a1.uObjectDescID].uSpeed, 0); if ( v91 != -1 ) { pAudioPlayer->PlaySound((SoundID)word_4EE088_sound_ids[uSpellID], PID(OBJECT_Item, v91), 0, -1, 0, 0, 0, 0); return; } return; break; case SPELL_FIRE_HASTE: if (masteryLevel == 1 || masteryLevel == 2) v39 = 60 * (realPoints + 60); else if (masteryLevel == 3 ) v39 = 180 * (realPoints + 20); else if (masteryLevel == 4 ) v39 = 240 * (realPoints + 15); else v39 = 0; actorPtr->pActorBuffs[ACTOR_BUFF_HASTE].Apply(pParty->uTimePlayed + (signed int)(signed __int64)((double)(v39 << 7) * 0.033333335), masteryLevel, 0, 0, 0); pEngine->pStru6Instance->_4A7E89_sparkles_on_actor_after_it_casts_buff(actorPtr, 0xFF3C1Eu); pAudioPlayer->PlaySound((SoundID)SOUND_Haste, PID(OBJECT_Actor, uActorID), 0, -1, 0, 0, 0, 0); return; case SPELL_FIRE_METEOR_SHOWER: if ( uCurrentlyLoadedLevelType == LEVEL_Indoor ) return; v114 = pParty->vPosition.z + 2500; v23 = 8; if (masteryLevel == 2) v23 = 10; else if (masteryLevel == 3) v23 = 12; else if (masteryLevel == 4) v23 = 14; spellnumb = 0; v28 = 0; for ( int i = 0; i < v23; i++) { v30 = rand() % 1000; spellnumc = v30 - 2500; v120 = v28 * v28; v119 = spellnumb * spellnumb; if ( sqrt((float)(spellnumc * spellnumc + v119 + v120)) <= 1.0 ) { v32 = 0; pitch = 0; } else { v31 = (signed __int64)sqrt((float)(v119 + v120)); v32 = stru_5C6E00->Atan2(spellnumb, (int)v28); pitch = stru_5C6E00->Atan2(v31, (int)spellnumc); } a1.stru_24.Reset(); a1.uType = stru_4E3ACC[uSpellID].uType; a1.uObjectDescID = GetObjDescId(uSpellID); a1.spell_level = uSkillLevel; a1.vPosition.x = pParty->vPosition.x; a1.vPosition.y = pParty->vPosition.y; a1.vPosition.z = v30 + v114; a1.spell_id = SPELL_FIRE_METEOR_SHOWER; a1.spell_skill = 0; a1.uAttributes = 0; a1.uSectorID = 0; a1.uSpriteFrameID = 0; a1.spell_caster_pid = PID(OBJECT_Actor, uActorID); a1.spell_target_pid = 0; a1.field_60_distance_related_prolly_lod = stru_50C198._427546(v30 + 2500); a1.uFacing = v32; a1.uSoundID = 0; if (pDir->uDistance < 307.2 ) a1.field_60_distance_related_prolly_lod = 0; else if ( pDir->uDistance < 1024 ) a1.field_60_distance_related_prolly_lod = 1; else if ( pDir->uDistance < 2560 ) a1.field_60_distance_related_prolly_lod = 2; else a1.field_60_distance_related_prolly_lod = 3; a1.field_61 = 2; v36 = a1.Create(v32, pitch, pObjectList->pObjects[(signed __int16)a1.uObjectDescID].uSpeed, 0); if ( v36 != -1 ) pAudioPlayer->PlaySound((SoundID)word_4EE088_sound_ids[9], PID(OBJECT_Item, v36), 0, -1, 0, 0, 0, 0); spellnumb = rand() % 1024 - 512; v28 = rand() % 1024 - 512; } return; break; case SPELL_AIR_SPARKS: if (masteryLevel == 2 ) v10 = 5; else if (masteryLevel == 3 ) v10 = 7; else if (masteryLevel == 4 ) v10 = 9; else v10 = 3; spellnuma = (signed int)(60 * stru_5C6E00->uIntegerDoublePi) / 360; a1.uType = stru_4E3ACC[uSpellID].uType; v118 = (signed int)(60 * stru_5C6E00->uIntegerDoublePi) / 360 / (v10 - 1); a1.uObjectDescID = GetObjDescId(uSpellID); a1.stru_24.Reset(); a1.spell_id = SPELL_AIR_SPARKS; a1.spell_level = uSkillLevel; a1.vPosition.x = actorPtr->vPosition.x; a1.spell_skill = 0; a1.vPosition.y = actorPtr->vPosition.y; a1.vPosition.z = actorPtr->vPosition.z + ((signed int)actorPtr->uActorHeight >> 1); a1.uFacing = pDir->uYawAngle; a1.uSoundID = 0; a1.uAttributes = 0; a1.uSectorID = pIndoor->GetSector(a1.vPosition.x, a1.vPosition.y, a1.vPosition.z); a1.spell_caster_pid = PID(OBJECT_Actor, uActorID); a1.uSpriteFrameID = 0; a1.spell_target_pid = 0; a1.field_60_distance_related_prolly_lod = 3; v19 = spellnuma / -2; a1a = spellnuma / 2; if ( spellnuma / -2 > spellnuma / 2 ) v20 = spellnuma / 2; else { do { a1.uFacing = v19 + LOWORD(pDir->uYawAngle); v20 = a1.Create((signed __int16)a1.uFacing, pDir->uPitchAngle, pObjectList->pObjects[(signed __int16)a1.uObjectDescID].uSpeed, 0); v19 += v118; } while ( v19 <= a1a ); } if ( v20 != -1 ) { pAudioPlayer->PlaySound((SoundID)word_4EE088_sound_ids[15], PID(OBJECT_Item, v20), 0, -1, 0, 0, 0, 0); return; } return; break; case SPELL_AIR_SHIELD: if (masteryLevel == 1 || masteryLevel == 2) v8 = 300 * realPoints + 3840; else if (masteryLevel == 3 ) v8 = 900 * realPoints + 3840; else if (masteryLevel == 4 ) v8 = 3600 * (realPoints + 64); else v8 = 0; actorPtr->pActorBuffs[ACTOR_BUFF_SHIELD].Apply( pParty->uTimePlayed + (signed int)(signed __int64)((double)(v8 << 7) * 0.033333335), masteryLevel, 0, 0, 0); return; case SPELL_EARTH_STONESKIN: if (masteryLevel == 1 || masteryLevel == 2) v44 = 300 * realPoints + 3840; else if (masteryLevel == 3 ) v44 = 900 * realPoints + 3840; else if (masteryLevel == 4 ) v44 = 3600 * (realPoints + 64); else v44 = 0; actorPtr->pActorBuffs[ACTOR_BUFF_STONESKIN].Apply( pParty->uTimePlayed + (signed int)(signed __int64)((double)(v44 << 7) * 0.033333335), masteryLevel, realPoints + 5, 0, 0); pEngine->pStru6Instance->_4A7E89_sparkles_on_actor_after_it_casts_buff(actorPtr,0x5C310Eu); pAudioPlayer->PlaySound((SoundID)SOUND_Stoneskin, PID(OBJECT_Actor,uActorID), 0, -1, 0, 0, 0, 0); return; case SPELL_SPIRIT_BLESS: if (masteryLevel == 1 || masteryLevel == 2) v42 = 300 * realPoints + 3840; else if (masteryLevel == 3 ) v42 = 900 * realPoints + 3840; else if (masteryLevel == 4 ) v42 = 1200 * realPoints + 3840; else v42 = 0; actorPtr->pActorBuffs[ACTOR_BUFF_BLESS].Apply( pParty->uTimePlayed + (signed int)(signed __int64)((double)(v42 << 7) * 0.033333335), masteryLevel, realPoints + 5, 0, 0); pEngine->pStru6Instance->_4A7E89_sparkles_on_actor_after_it_casts_buff(actorPtr,0xC8C805u); pAudioPlayer->PlaySound((SoundID)SOUND_Bless, PID(OBJECT_Actor,uActorID), 0, -1, 0, 0, 0, 0); return; break; case SPELL_SPIRIT_FATE: if (masteryLevel == 1 || masteryLevel == 2) v48 = 2 * realPoints + 40; else if (masteryLevel == 3 ) v48 = 3 * realPoints + 60; else if (masteryLevel == 4 ) v48 = 2 * (3 * realPoints + 60); else v48 = 0; actorPtr->pActorBuffs[ACTOR_BUFF_FATE].Apply(pParty->uTimePlayed + 1280, masteryLevel, v48, 0, 0); pEngine->pStru6Instance->_4A7E89_sparkles_on_actor_after_it_casts_buff(actorPtr,0xC8C805u); pAudioPlayer->PlaySound((SoundID)SOUND_Fate, PID(OBJECT_Actor, uActorID), 0, -1, 0, 0, 0, 0); return; case SPELL_SPIRIT_HEROISM: if (masteryLevel == 1 || masteryLevel == 2) v54 = 300 * realPoints + 3840; else if (masteryLevel == 3 ) v54 = 900 * realPoints + 3840; else if (masteryLevel == 4 ) v54 = 1200 * realPoints + 3840; else v54 = 0; actorPtr->pActorBuffs[ACTOR_BUFF_HEROISM].Apply( pParty->uTimePlayed + (signed int)(signed __int64)((double)(v54 << 7) * 0.033333335), masteryLevel, realPoints + 5, 0, 0); pEngine->pStru6Instance->_4A7E89_sparkles_on_actor_after_it_casts_buff(actorPtr,0xC8C805u); pAudioPlayer->PlaySound((SoundID)SOUND_51heroism03, PID(OBJECT_Actor,uActorID), 0, -1, 0, 0, 0, 0); return; case SPELL_BODY_HAMMERHANDS: if ( (signed int)masteryLevel <= 0 || (signed int)masteryLevel > 4 ) v51 = 0; else v51 = 3600 * realPoints; actorPtr->pActorBuffs[ACTOR_BUFF_PAIN_HAMMERHANDS].Apply( pParty->uTimePlayed + (signed int)(signed __int64)((double)(v51 << 7) * 0.033333335), masteryLevel, realPoints, 0, 0); pEngine->pStru6Instance->_4A7E89_sparkles_on_actor_after_it_casts_buff(actorPtr, 0xA81376u); pAudioPlayer->PlaySound((SoundID)SOUND_51heroism03, PID(OBJECT_Actor, uActorID), 0, -1, 0, 0, 0, 0); return; case SPELL_BODY_POWER_CURE: actorPtr->sCurrentHP += 5 * realPoints + 10; if ( actorPtr->sCurrentHP >= (signed int)actorPtr->pMonsterInfo.uHP ) actorPtr->sCurrentHP = LOWORD(actorPtr->pMonsterInfo.uHP); pEngine->pStru6Instance->_4A7E89_sparkles_on_actor_after_it_casts_buff(actorPtr, 0xA81376u); pAudioPlayer->PlaySound((SoundID)SOUND_Fate, PID(OBJECT_Actor, uActorID), 0, -1, 0, 0, 0, 0); return; case SPELL_LIGHT_DISPEL_MAGIC: for (int i = 0; i < 20; i++ ) pParty->pPartyBuffs[i].Reset(); for (int i = 1; i <= 4; i++) { v59 = pPlayers[i]->GetParameterBonus(pPlayers[i]->GetActualWillpower()); v61 = (pPlayers[i]->GetParameterBonus(pPlayers[i]->GetActualIntelligence()) + v59) / 2; v63 = v61 + pPlayers[i]->GetParameterBonus(pPlayers[i]->GetActualLuck()) + 30; if ( rand() % v63 < 30 ) { for (uint k = 0; k < pPlayers[i]->pPlayerBuffs.size(); k++) pPlayers[i]->pPlayerBuffs[k].Reset(); pOtherOverlayList->_4418B1(11210, i + 99, 0, 65536); } } pAudioPlayer->PlaySound((SoundID)word_4EE088_sound_ids[80], PID(OBJECT_Actor, uActorID), 0, -1, 0, 0, 0, 0); return; case SPELL_LIGHT_DAY_OF_PROTECTION: if (masteryLevel == 1 || masteryLevel == 2) v96 = 300 * realPoints + 3840; else if (masteryLevel == 3 ) { LOWORD(realPoints) = 3 * realPoints; v96 = 900 * (uSkillLevel & 0x3F) + 3840; } else if (masteryLevel == 4 ) { v96 = 1200 * realPoints + 3840; LOWORD(realPoints) = 4 * realPoints; } else { LOWORD(realPoints) = uSkillLevel; v96 = 0; } actorPtr->pActorBuffs[ACTOR_BUFF_DAY_OF_PROTECTION].Apply( pParty->uTimePlayed + (signed int)(signed __int64)((double)(v96 << 7) * 0.033333335), masteryLevel, realPoints, 0, 0); pEngine->pStru6Instance->_4A7E89_sparkles_on_actor_after_it_casts_buff(actorPtr, 0xFFFFFFu); pAudioPlayer->PlaySound((SoundID)SOUND_94dayofprotection03, PID(OBJECT_Actor, uActorID), 0, -1, 0, 0, 0, 0); return; case SPELL_LIGHT_HOUR_OF_POWER: if (masteryLevel == 1 || masteryLevel == 2) v94 = 300 * realPoints + 3840; else if (masteryLevel == 3) v94 = 900 * realPoints + 3840; else if (masteryLevel == 4) v94 = 1200 * realPoints + 3840; else v94 = 0; actorPtr->pActorBuffs[ACTOR_BUFF_HOUR_OF_POWER].Apply( pParty->uTimePlayed + (signed int)(signed __int64)((double)(v94 << 7) * 0.033333335), masteryLevel, realPoints + 5, 0, 0); pEngine->pStru6Instance->_4A7E89_sparkles_on_actor_after_it_casts_buff(actorPtr, 0xFFFFFFu); pAudioPlayer->PlaySound((SoundID)SOUND_9armageddon01, PID(OBJECT_Actor, uActorID), 0, -1, 0, 0, 0, 0); return; case SPELL_DARK_SHARPMETAL: if (masteryLevel == 2) v70 = 5; else if (masteryLevel == 3) v70 = 7; else if (masteryLevel == 4) v70 = 9; else v70 = 3; spellnume = (signed int)(60 * stru_5C6E00->uIntegerDoublePi) / 360; a1.uType = stru_4E3ACC[uSpellID].uType; v116 = (signed int)(60 * stru_5C6E00->uIntegerDoublePi) / 360 / (v70 - 1); a1.uObjectDescID = GetObjDescId(uSpellID); a1.stru_24.Reset(); a1.spell_id = uSpellID; a1.spell_level = uSkillLevel; a1.vPosition.x = actorPtr->vPosition.x; a1.spell_skill = 0; a1.vPosition.y = actorPtr->vPosition.y; a1.vPosition.z = actorPtr->vPosition.z + ((signed int)actorPtr->uActorHeight >> 1); a1.uFacing = pDir->uYawAngle; a1.uSoundID = 0; a1.uAttributes = 0; a1.uSectorID = pIndoor->GetSector(a1.vPosition.x, a1.vPosition.y, a1.vPosition.z); a1.spell_caster_pid = PID(OBJECT_Actor, uActorID); a1.uSpriteFrameID = 0; a1.spell_target_pid = 0; a1.field_60_distance_related_prolly_lod = 3; a1c = spellnume / -2; if ( spellnume / -2 > spellnume / 2 ) v80 = spellnume / -2; else { do { v79 = pDir->uYawAngle; a1.uFacing = a1c + LOWORD(pDir->uYawAngle); v80 = a1.Create(v79, pDir->uPitchAngle, pObjectList->pObjects[(signed __int16)a1.uObjectDescID].uSpeed, 0); a1c += v116; } while ( a1c <= spellnume / 2 ); } if ( v80 != -1 ) { pAudioPlayer->PlaySound((SoundID)word_4EE088_sound_ids[93], PID(OBJECT_Item, v80), 0, -1, 0, 0, 0, 0); return; } return; break; case SPELL_DARK_PAIN_REFLECTION: if (masteryLevel == 0) v68 = 0; else if (masteryLevel == 1 || (masteryLevel == 2) || (masteryLevel == 3)) v68 = 300 * realPoints + 3840; else v68 = 900 * realPoints + 3840; actorPtr->pActorBuffs[ACTOR_BUFF_PAIN_REFLECTION].Apply( pParty->uTimePlayed + (signed int)(signed __int64)((double)(v68 << 7) * 0.033333335), masteryLevel, 0, 0, 0); pEngine->pStru6Instance->_4A7E89_sparkles_on_actor_after_it_casts_buff(actorPtr,0x7E7E7Eu); pAudioPlayer->PlaySound((SoundID)SOUND_Sacrifice2, PID(OBJECT_Actor, uActorID), 0, -1, 0, 0, 0, 0); return; } } //----- (new func) -------------------------------------------------------- unsigned short Actor::GetObjDescId( int spellId ) { for (unsigned int i = 0; i < pObjectList->uNumObjects; i++) { if (stru_4E3ACC[spellId].uType == pObjectList->pObjects[i].uObjectID) { return i; break; } } return 0; } //----- (0043ABB0) -------------------------------------------------------- bool Actor::ArePeasantsOfSameFaction(Actor *a1, Actor *a2) { unsigned int v2; // esi@1 unsigned int v3; // edi@1 v2 = a1->uAlly; if ( !a1->uAlly ) v2 = (a1->pMonsterInfo.uID - 1) / 3 + 1; v3 = a2->uAlly; if ( !a2->uAlly ) v3 = (a2->pMonsterInfo.uID - 1) / 3 + 1; if ( v2 >= 39 && v2 <= 44 && v3 >= 39 && v3 <= 44 || v2 >= 45 && v2 <= 50 && v3 >= 45 && v3 <= 50 || v2 >= 51 && v2 <= 62 && v3 >= 51 && v3 <= 62 || v2 >= 78 && v2 <= 83 && v3 >= 78 && v3 <= 83 || v2 == v3 ) return true; else return false; } //----- (0043AC45) -------------------------------------------------------- void Actor::AggroSurroundingPeasants(unsigned int uActorID, int a2) { int v4; // ebx@8 int v5; // ST1C_4@8 int v6; // eax@8 int x = 0; x |= 0x80000; int y = 0; y |= 0x80000; Actor* victim = &pActors[uActorID]; if ( a2 == 1 ) victim->uAttributes |= ACTOR_AGGRESSOR; for (uint i = 0; i < uNumActors; ++i) { Actor* actor = &pActors[i]; if (!actor->CanAct() || i == uActorID) continue; if (Actor::ArePeasantsOfSameFaction(victim, actor)) { v4 = abs(actor->vPosition.x - victim->vPosition.x); v5 = abs(actor->vPosition.y - victim->vPosition.y); v6 = abs(actor->vPosition.z - victim->vPosition.z); if (int_get_vector_length(v4, v5, v6) < 4096) { actor->pMonsterInfo.uHostilityType = MonsterInfo::Hostility_Long; if ( a2 == 1 ) actor->uAttributes |= ACTOR_AGGRESSOR; } } } } //----- (00404874) -------------------------------------------------------- void Actor::AI_RangedAttack( unsigned int uActorID, struct AIDirection *pDir, int type, char a4 ) { char specAb; // al@1 int v13; // edx@28 SpriteObject a1; // [sp+Ch] [bp-74h]@1 switch ( type ) { case 1: a1.uType = 545; break; case 2: a1.uType = 550; break; case 3: a1.uType = 510; break; case 4: a1.uType = 500; break; case 5: a1.uType = 515; break; case 6: a1.uType = 505; break; case 7: a1.uType = 530; break; case 8: a1.uType = 525; break; case 9: a1.uType = 520; break; case 10: a1.uType = 535; break; case 11: a1.uType = 540; break; case 13: a1.uType = 555; break; default: return; } bool found = false; for ( uint i = 0; i < pObjectList->uNumObjects; i++) { if (pObjectList->pObjects[i].uObjectID == a1.uType) { a1.uObjectDescID = i; found = true; break; } } if (!found) { Error("Item not found"); return; } a1.stru_24.Reset(); a1.spell_id = 0; a1.vPosition.x = pActors[uActorID].vPosition.x; a1.vPosition.y = pActors[uActorID].vPosition.y; a1.vPosition.z = pActors[uActorID].vPosition.z - (unsigned int)(pActors[uActorID].uActorHeight * -0.75); a1.spell_level = 0; a1.spell_skill = 0; a1.uFacing = pDir->uYawAngle; a1.uSoundID = 0; a1.uAttributes = 0; a1.uSectorID = pIndoor->GetSector(a1.vPosition.x, a1.vPosition.y, a1.vPosition.z); a1.uSpriteFrameID = 0; a1.spell_caster_pid = PID(OBJECT_Actor, uActorID); a1.spell_target_pid = 0; if (pDir->uDistance < 307.2 ) a1.field_60_distance_related_prolly_lod = 0; else if ( pDir->uDistance < 1024 ) a1.field_60_distance_related_prolly_lod = 1; else if ( pDir->uDistance < 2560 ) a1.field_60_distance_related_prolly_lod = 2; else a1.field_60_distance_related_prolly_lod = 3; a1.field_61 = a4; a1.Create(pDir->uYawAngle, pDir->uPitchAngle, pObjectList->pObjects[(signed __int16)a1.uObjectDescID].uSpeed, 0); if ( pActors[uActorID].pMonsterInfo.uSpecialAbilityType == 1 ) { specAb = pActors[uActorID].pMonsterInfo.uSpecialAbilityDamageDiceBonus; if ( specAb == 2 ) { a1.vPosition.z += 40; v13 = pDir->uYawAngle; } else { if ( specAb != 3 ) return; a1.Create(pDir->uYawAngle + 30, //TODO find out why the YawAngle change pDir->uPitchAngle, pObjectList->pObjects[(signed __int16)a1.uObjectDescID].uSpeed, 0); v13 = pDir->uYawAngle - 30; } a1.Create(v13, pDir->uPitchAngle, pObjectList->pObjects[(signed __int16)a1.uObjectDescID].uSpeed, 0); } return; } //----- (00404736) -------------------------------------------------------- void Actor::Explode( unsigned int uActorID ) { SpriteObject a1; // [sp+Ch] [bp-78h]@1 a1.uType = 600; a1.uObjectDescID = GetObjDescId(a1.uType); a1.stru_24.Reset(); a1.spell_id = 0; a1.spell_level = 0; a1.spell_skill = 0; a1.vPosition.x = pActors[uActorID].vPosition.x; a1.vPosition.y = pActors[uActorID].vPosition.y; a1.vPosition.z = pActors[uActorID].vPosition.z - (unsigned int)(pActors[uActorID].uActorHeight * -0.75); a1.uFacing = 0; a1.uSoundID = 0; a1.uAttributes = 0; a1.uSectorID = pIndoor->GetSector(a1.vPosition.x, a1.vPosition.y, a1.vPosition.z); a1.uSpriteFrameID = 0; a1.spell_caster_pid = PID(OBJECT_Actor, uActorID); a1.spell_target_pid = 0; a1.field_60_distance_related_prolly_lod = 3; a1.field_61 = 4; a1.Create(0, 0, 0, 0); return; } //----- (004040E9) -------------------------------------------------------- // // Get direction vector from object1 to object2, // // distance from object1 to object2 and Euler angles of the direction vector // // // // // // object1 & object2 format : objectType | (objectID << 3) // // objectType == 2 - SpriteObject // // objectType == 3 - Actor // // objectType == 4 - Party // // objectType == 5 - Decoration // // // // originally this function had following prototype: // // struct DirectionInfo GetDirectionInfo(signed int object1, signed int object2, signed int a4) // // but compiler converts functions returning structures by value in the such way void Actor::GetDirectionInfo( unsigned int uObj1ID, unsigned int uObj2ID, struct AIDirection *pOut, int a4 ) { signed int v4; // eax@1 signed int v5; // ecx@1 int v18; // edx@15 float v31; // st7@45 float v32; // st6@45 float v33; // st7@45 Vec3_int_ v37; // [sp-10h] [bp-5Ch]@15 AIDirection v41; // [sp+14h] [bp-38h]@46 float outy2; // [sp+38h] [bp-14h]@33 float outx2; // [sp+3Ch] [bp-10h]@33 int outz; // [sp+40h] [bp-Ch]@6 int outy; // [sp+44h] [bp-8h]@6 int outx; // [sp+48h] [bp-4h]@6 float a4a; // [sp+58h] [bp+Ch]@45 v4 = PID_ID(uObj1ID); //v6 = uObj2ID; v5 = PID_ID(uObj2ID); switch( PID_TYPE(uObj1ID) ) { case OBJECT_Item: { outx = pSpriteObjects[v4].vPosition.x; outy = pSpriteObjects[v4].vPosition.y; outz = pSpriteObjects[v4].vPosition.z; break; } case OBJECT_Actor: { outx = pActors[v4].vPosition.x; outy = pActors[v4].vPosition.y; outz = pActors[v4].vPosition.z - (unsigned int)(signed __int64)((double)pActors[v4].uActorHeight * -0.75); break; } case OBJECT_Player: { if ( !v4 ) { outx = pParty->vPosition.x; outy = pParty->vPosition.y; outz = pParty->vPosition.z + (signed int)pParty->uPartyHeight / 3; break; } if ( v4 == 4 ) { v18 = pParty->sRotationY - stru_5C6E00->uIntegerHalfPi; v37.z = pParty->vPosition.z + (signed int)pParty->uPartyHeight / 3; v37.x = pParty->vPosition.x; v37.y = pParty->vPosition.y; Vec3_int_::Rotate(24, v18, 0, v37, &outx, &outy, &outz); break; } if ( v4 == 3 ) { v18 = pParty->sRotationY - stru_5C6E00->uIntegerHalfPi; v37.z = pParty->vPosition.z + (signed int)pParty->uPartyHeight / 3; v37.x = pParty->vPosition.x; v37.y = pParty->vPosition.y; Vec3_int_::Rotate(8, v18, 0, v37, &outx, &outy, &outz); break; } if ( v4 == 2 ) { v37.z = pParty->vPosition.z + (signed int)pParty->uPartyHeight / 3; v18 = stru_5C6E00->uIntegerHalfPi + pParty->sRotationY; v37.x = pParty->vPosition.x; v37.y = pParty->vPosition.y; Vec3_int_::Rotate(8, v18, 0, v37, &outx, &outy, &outz); break; } if ( v4 == 1 ) { v37.z = pParty->vPosition.z + (signed int)pParty->uPartyHeight / 3; v18 = stru_5C6E00->uIntegerHalfPi + pParty->sRotationY; v37.x = pParty->vPosition.x; v37.y = pParty->vPosition.y; Vec3_int_::Rotate(24, v18, 0, v37, &outx, &outy, &outz); break; } } case OBJECT_Decoration: { outx = pLevelDecorations[v4].vPosition.x; outy = pLevelDecorations[v4].vPosition.y; outz = pLevelDecorations[v4].vPosition.z; break; } default: { outz = 0; outy = 0; outx = 0; break; } case OBJECT_BModel: { if ( uCurrentlyLoadedLevelType == LEVEL_Indoor ) { outx = (pIndoor->pFaces[v4].pBounding.x1 + pIndoor->pFaces[v4].pBounding.x2) >> 1; outy = (pIndoor->pFaces[v4].pBounding.y1 + pIndoor->pFaces[v4].pBounding.y2) >> 1; outz = (pIndoor->pFaces[v4].pBounding.z1 + pIndoor->pFaces[v4].pBounding.z2) >> 1; } break; } } switch( PID_TYPE(uObj2ID) ) { case OBJECT_Item: { outx2 = (float)pSpriteObjects[v5].vPosition.x; outy2 =(float) pSpriteObjects[v5].vPosition.y; a4 = pSpriteObjects[v5].vPosition.z; break; } case OBJECT_Actor: { outx2 = (float)pActors[v5].vPosition.x; outy2 = (float)pActors[v5].vPosition.y; a4 = pActors[v5].vPosition.z - (unsigned int)(signed __int64)((double)pActors[v5].uActorHeight * -0.75); break; } case OBJECT_Player: { outx2 = (float)pParty->vPosition.x; outy2 = (float)pParty->vPosition.y; if ( !a4 ) a4 = pParty->sEyelevel; a4 = pParty->vPosition.z + a4; break; } case OBJECT_Decoration: { outx2 = (float)pLevelDecorations[v5].vPosition.x; outy2 = (float)pLevelDecorations[v5].vPosition.y; a4 = pLevelDecorations[v5].vPosition.z; break; } default: { outx2 = 0.0; outy2 = 0.0; a4 = 0; break; } case OBJECT_BModel: { if ( uCurrentlyLoadedLevelType == LEVEL_Indoor ) { outx2 = (float)((pIndoor->pFaces[v5].pBounding.x1 + pIndoor->pFaces[v5].pBounding.x2) >> 1); outy2 = (float)((pIndoor->pFaces[v5].pBounding.y1 + pIndoor->pFaces[v5].pBounding.y2) >> 1); a4 = (pIndoor->pFaces[v5].pBounding.z1 + pIndoor->pFaces[v5].pBounding.z2) >> 1; } break; } } v31 = (float)outx2 - (float)outx; v32 = (float)outy2 - (float)outy; a4a = (float)a4 - (float)outz; outx2 = v32 * v32; outy2 = v31 * v31; v33 = sqrt(a4a * a4a + outy2 + outx2); if ( v33 <= 1.0 ) { pOut->vDirection.x = 65536; pOut->vDirection.y = 0; pOut->vDirection.z = 0; pOut->uDistance = 1; pOut->uDistanceXZ = 1; pOut->uYawAngle = 0; pOut->uPitchAngle = 0; } else { pOut->vDirection.x = (int32_t)(1.0 / v33 * v31 * 65536.0); pOut->vDirection.y = (int32_t)(1.0 / v33 * v32 * 65536.0); pOut->vDirection.z = (int32_t)(1.0 / v33 * a4a * 65536.0); pOut->uDistance = (uint)v33; pOut->uDistanceXZ = (uint)sqrt(outy2 + outx2); pOut->uYawAngle = stru_5C6E00->Atan2((signed __int64)v31, (signed __int64)v32); pOut->uPitchAngle = stru_5C6E00->Atan2(pOut->uDistanceXZ, (signed __int64)a4a); } } //----- (00404030) -------------------------------------------------------- void Actor::AI_FaceObject(unsigned int uActorID, unsigned int uObjID, int _48, AIDirection *a4) { AIDirection *v7; // eax@3 AIDirection v1; // eax@3 AIDirection a3; // [sp+8h] [bp-38h]@4 if ( rand() % 100 >= 5 ) { //v9 = &pActors[uActorID]; if ( !a4 ) { Actor::GetDirectionInfo(PID(OBJECT_Actor, uActorID), uObjID, &v1, 0); v7 = &v1; } else v7 = a4; pActors[uActorID].uYawAngle = v7->uYawAngle; pActors[uActorID].uCurrentActionTime = 0; pActors[uActorID].vVelocity.z = 0; pActors[uActorID].vVelocity.y = 0; pActors[uActorID].vVelocity.x = 0; pActors[uActorID].uPitchAngle = v7->uPitchAngle; pActors[uActorID].uCurrentActionLength = 256; pActors[uActorID].uAIState = Interacting; pActors[uActorID].UpdateAnimation(); } else Actor::AI_Bored(uActorID, uObjID, a4); } //----- (00403F58) -------------------------------------------------------- void Actor::AI_StandOrBored(unsigned int uActorID, signed int uObjID, int uActionLength, AIDirection *a4) { if (rand() % 2)//0 or 1 AI_Bored(uActorID, uObjID, a4); else AI_Stand(uActorID, uObjID, uActionLength, a4); } //----- (00403EB6) -------------------------------------------------------- void Actor::AI_Stand(unsigned int uActorID, unsigned int object_to_face_pid, unsigned int uActionLength, AIDirection *a4) { assert(uActorID < uNumActors); // Actor* actor = &pActors[uActorID]; AIDirection a3; if (!a4) { Actor::GetDirectionInfo(PID(OBJECT_Actor, uActorID), object_to_face_pid, &a3, 0); a4 = &a3; } pActors[uActorID].uAIState = Standing; if (!uActionLength) pActors[uActorID].uCurrentActionLength = rand() % 256 + 256;// от 256 до 256 + 256 else pActors[uActorID].uCurrentActionLength = uActionLength; pActors[uActorID].uCurrentActionTime = 0; pActors[uActorID].uYawAngle = a4->uYawAngle; pActors[uActorID].uPitchAngle = a4->uPitchAngle; pActors[uActorID].vVelocity.z = 0; pActors[uActorID].vVelocity.y = 0; pActors[uActorID].vVelocity.x = 0; pActors[uActorID].UpdateAnimation(); } //----- (00403E61) -------------------------------------------------------- void __fastcall Actor::StandAwhile(unsigned int uActorID) { pActors[uActorID].uCurrentActionLength = rand() % 128 + 128; pActors[uActorID].uCurrentActionTime = 0; pActors[uActorID].uAIState = Standing; pActors[uActorID].vVelocity.z = 0; pActors[uActorID].vVelocity.y = 0; pActors[uActorID].vVelocity.x = 0; pActors[uActorID].UpdateAnimation(); } //----- (00403C6C) -------------------------------------------------------- void Actor::AI_MeleeAttack(unsigned int uActorID, signed int sTargetPid, struct AIDirection *arg0) { int16_t v6; // esi@6 int16_t v7; // edi@6 signed int v8; // eax@7 Vec3_int_ v10; // ST04_12@9 AIDirection *v12; // eax@11 AIDirection a3; // [sp+Ch] [bp-48h]@12 AIDirection v20; // [sp+28h] [bp-2Ch]@12 int v23; // [sp+4Ch] [bp-8h]@6 unsigned int v25; // [sp+5Ch] [bp+8h]@13 assert(uActorID < uNumActors); if ( pActors[uActorID].pMonsterInfo.uMovementType == MONSTER_MOVEMENT_TYPE_STAIONARY && pActors[uActorID].pMonsterInfo.uAIType == 1 ) { Actor::AI_Stand(uActorID, sTargetPid, 0, arg0); return; } if ( PID_TYPE(sTargetPid) == OBJECT_Actor) { v8 = PID_ID(sTargetPid); v6 = pActors[v8].vPosition.x; v7 = pActors[v8].vPosition.y; v23 = (int)(pActors[v8].uActorHeight * 0.75 + pActors[v8].vPosition.z); } else if ( PID_TYPE(sTargetPid) == OBJECT_Player) { v6 = pParty->vPosition.x; v7 = pParty->vPosition.y; v23 = pParty->vPosition.z + pParty->sEyelevel; } else { Error("Should not get here"); return; } v10.x = pActors[uActorID].vPosition.x; v10.y = pActors[uActorID].vPosition.y; v10.z = (int32_t)(pActors[uActorID].uActorHeight * 0.75 + pActors[uActorID].vPosition.z); if ( sub_407A1C((int)v6, (int)v7, v23, v10) ) { if (arg0 != nullptr) v12 = arg0; else { Actor::GetDirectionInfo(PID(OBJECT_Actor, uActorID), sTargetPid, &a3, 0); v12 = &a3; } pActors[uActorID].uYawAngle = LOWORD(v12->uYawAngle); pActors[uActorID].uCurrentActionLength = pSpriteFrameTable->pSpriteSFrames[pActors[uActorID].pSpriteIDs[ANIM_AtkMelee]].uAnimLength * 8; pActors[uActorID].uCurrentActionTime = 0; pActors[uActorID].uAIState = AttackingMelee; Actor::PlaySound(uActorID, 0); v25 = pMonsterStats->pInfos[pActors[uActorID].pMonsterInfo.uID].uRecoveryTime; if ( pActors[uActorID].pActorBuffs[ACTOR_BUFF_SLOWED].uExpireTime > 0 ) v25 *= 2; if ( pParty->bTurnBasedModeOn != 1 ) pActors[uActorID].pMonsterInfo.uRecoveryTime = (int)(flt_6BE3A8_debug_recmod2 * v25 * 2.133333333333333); else pActors[uActorID].pMonsterInfo.uRecoveryTime = v25; pActors[uActorID].vVelocity.z = 0; pActors[uActorID].vVelocity.y = 0; pActors[uActorID].vVelocity.x = 0; pActors[uActorID].UpdateAnimation(); } else Actor::AI_Pursue1(uActorID, sTargetPid, rand() % 2, 64, arg0); } //----- (00438CF3) -------------------------------------------------------- void Actor::ApplyFineForKillingPeasant(unsigned int uActorID) { if ( uLevelMapStatsID == 0 || !pActors[uActorID].IsPeasant()) return; if ( (uLevelMapStatsID == 6 || uLevelMapStatsID == 7) && pParty->IsPartyEvil()) //celeste and bracada return; if ( (uLevelMapStatsID == 5 || uLevelMapStatsID == 8) && pParty->IsPartyGood()) // the pit and deyja return; pParty->uFine += 100 * (pMapStats->pInfos[uLevelMapStatsID]._steal_perm + pActors[uActorID].pMonsterInfo.uLevel + pParty->GetPartyReputation()); if ( pParty->uFine < 0 ) pParty->uFine = 0; if ( pParty->uFine > 4000000 ) pParty->uFine = 4000000; if (uCurrentlyLoadedLevelType == LEVEL_Outdoor) { if (pOutdoor->ddm.uReputation < 10000) pOutdoor->ddm.uReputation++; } else if (uCurrentlyLoadedLevelType == LEVEL_Indoor) { if (pIndoor->dlv.uReputation < 10000) pIndoor->dlv.uReputation++; } else assert(false); if ( pParty->uFine ) { for ( int i = 1; i <= 4; i++) { if ( !_449B57_test_bit(pPlayers[i]->_achieved_awards_bits, 1) ) _449B7E_toggle_bit(pPlayers[i]->_achieved_awards_bits, 1, 1u); } } } //----- (0043AE80) -------------------------------------------------------- void Actor::AddBloodsplatOnDamageOverlay(unsigned int uActorID, int a2, signed int a3) { unsigned int v4; // esi@1 v4 = PID(OBJECT_Actor,uActorID); switch ( a2 ) { case 1: if ( a3 ) pOtherOverlayList->_4418B6(904, v4, 0, (int)(sub_43AE12(a3) * 65536.0), 0); return; case 2: if ( a3 ) pOtherOverlayList->_4418B6(905, v4, 0, (int)(sub_43AE12(a3) * 65536.0), 0); return; case 3: if ( a3 ) pOtherOverlayList->_4418B6(906, v4, 0, (int)(sub_43AE12(a3) * 65536.0), 0); return; case 4: if ( a3 ) pOtherOverlayList->_4418B6(907, v4, 0, (int)(sub_43AE12(a3) * 65536.0), 0); return; case 5: pOtherOverlayList->_4418B6(901, v4, 0, PID(OBJECT_Actor,uActorID), 0); return; case 6: pOtherOverlayList->_4418B6(902, v4, 0, PID(OBJECT_Actor,uActorID), 0); return; case 7: pOtherOverlayList->_4418B6(903, v4, 0, PID(OBJECT_Actor,uActorID), 0); return; case 8: pOtherOverlayList->_4418B6(900, v4, 0, PID(OBJECT_Actor,uActorID), 0); return; case 9: pOtherOverlayList->_4418B6(909, v4, 0, PID(OBJECT_Actor,uActorID), 0); return; case 10: pOtherOverlayList->_4418B6(908, v4, 0, PID(OBJECT_Actor,uActorID), 0); return; default: return; } return; } //----- (0043B3E0) -------------------------------------------------------- int Actor::_43B3E0_CalcDamage( signed int dmgSource ) { signed int v2; // ebp@1 int v3; // eax@9 signed int v4; // edi@9 int v5; // esi@9 unsigned __int16 v8; // si@21 int v9; // edi@21 signed int v10; // eax@23 int v11; // [sp+10h] [bp-4h]@1 v2 = 0; v11 = 0; switch( dmgSource ) { case 0: if ( this->pActorBuffs[ACTOR_BUFF_HOUR_OF_POWER].uExpireTime > 0 ) v2 = this->pActorBuffs[ACTOR_BUFF_HOUR_OF_POWER].uPower; if ( this->pActorBuffs[ACTOR_BUFF_HEROISM].uExpireTime > 0 && this->pActorBuffs[ACTOR_BUFF_HEROISM].uPower > v2 ) v2 = this->pActorBuffs[ACTOR_BUFF_HEROISM].uPower; if ( this->pActorBuffs[ACTOR_BUFF_PAIN_HAMMERHANDS].uExpireTime > 0 ) v2 += this->pActorBuffs[ACTOR_BUFF_PAIN_HAMMERHANDS].uPower; v3 = this->pMonsterInfo.uAttack1DamageDiceRolls; v4 = this->pMonsterInfo.uAttack1DamageDiceSides; v5 = this->pMonsterInfo.uAttack1DamageBonus; break; case 1: v3 = this->pMonsterInfo.uAttack2DamageDiceRolls; v4 = this->pMonsterInfo.uAttack2DamageDiceSides; v5 = this->pMonsterInfo.uAttack2DamageBonus; break; case 2: v8 = this->pMonsterInfo.uSpellSkillAndMastery1; v9 = this->pMonsterInfo.uSpell1ID; v10 = SkillToMastery(v8); return _43AFE3_calc_spell_damage(v9, v8 & 0x3F, v10, 0); break; case 3: v8 = this->pMonsterInfo.uSpellSkillAndMastery2; v9 = this->pMonsterInfo.uSpell2ID; v10 = SkillToMastery(v8); return _43AFE3_calc_spell_damage(v9, v8 & 0x3F, v10, 0); break; case 4: v3 = this->pMonsterInfo.uSpecialAbilityDamageDiceRolls; v4 = this->pMonsterInfo.uSpecialAbilityDamageDiceSides; v5 = this->pMonsterInfo.uSpecialAbilityDamageDiceBonus; default: return 0; } for ( int i = 0; i < v3; i++) v11 += rand() % v4 + 1; return v11 + v5 + v2; } //----- (00438B9B) -------------------------------------------------------- bool Actor::IsPeasant() { unsigned int InHostile_Id; // eax@1 InHostile_Id = this->uAlly; if ( !this->uAlly ) InHostile_Id = (this->pMonsterInfo.uID - 1) / 3 + 1; return (signed int)InHostile_Id >= 39 && (signed int)InHostile_Id <= 44//Dwarfs peasants || (signed int)InHostile_Id >= 45 && (signed int)InHostile_Id <= 50//Elves peasants || (signed int)InHostile_Id >= 51 && (signed int)InHostile_Id <= 62//Humans peasants || (signed int)InHostile_Id >= 78 && (signed int)InHostile_Id <= 83;//Goblins peasants } //----- (0042EBEE) -------------------------------------------------------- void Actor::StealFrom( unsigned int uActorID ) { Player *pPlayer; // edi@1 int v4; // ebx@2 unsigned int v5; // eax@2 DDM_DLV_Header *v6; // esi@4 int v8; // [sp+8h] [bp-4h]@6 pPlayer = &pParty->pPlayers[uActiveCharacter-1]; if ( pPlayer->CanAct() ) { CastSpellInfoHelpers::_427D48(); v4 = 0; v5 = pMapStats->GetMapInfo(pCurrentMapName); if ( v5 ) v4 = pMapStats->pInfos[v5]._steal_perm; v6 = &pOutdoor->ddm; if ( uCurrentlyLoadedLevelType != LEVEL_Outdoor) v6 = &pIndoor->dlv; pPlayer->StealFromActor(uActorID, v4, v6->uReputation++); v8 = pPlayer->GetAttackRecoveryTime(0); if ( v8 < 30 ) v8 = 30; if ( !pParty->bTurnBasedModeOn ) pPlayer->SetRecoveryTime((int)(flt_6BE3A4_debug_recmod1 * v8 * 2.133333333333333)); pTurnEngine->ApplyPlayerAction(); } return; } //----- (00403A60) -------------------------------------------------------- void Actor::AI_SpellAttack2(unsigned int uActorID, signed int edx0, AIDirection *pDir) { Actor *v3; // ebx@1 int16_t v4; // esi@3 int16_t v5; // edi@3 signed int v6; // eax@4 Vec3_int_ v7; // ST04_12@6 AIDirection *v9; // eax@8 __int16 v13; // ax@10 AIDirection a3; // [sp+Ch] [bp-48h]@9 AIDirection v18; // [sp+28h] [bp-2Ch]@9 int v19; // [sp+44h] [bp-10h]@6 signed int a2; // [sp+48h] [bp-Ch]@1 int v21; // [sp+4Ch] [bp-8h]@3 unsigned int pDira; // [sp+5Ch] [bp+8h]@10 v3 = &pActors[uActorID]; a2 = edx0; if ( PID_TYPE(edx0) == OBJECT_Actor) { v6 = PID_ID(edx0); v4 = pActors[v6].vPosition.x; v5 = pActors[v6].vPosition.y; v21 = (int)(pActors[v6].uActorHeight * 0.75 + pActors[v6].vPosition.z); } else if ( PID_TYPE(edx0) == OBJECT_Player) { v4 = pParty->vPosition.x; v5 = pParty->vPosition.y; v21 = pParty->vPosition.z + pParty->sEyelevel; } else { Error("Should not get here"); return; } v19 = v3->uActorHeight; v7.z = v3->vPosition.z - (int)(v19 * -0.75); v7.y = v3->vPosition.y; v7.x = v3->vPosition.x; if ( sub_407A1C(v4, v5, v21, v7) ) { if ( pDir == nullptr) { Actor::GetDirectionInfo(PID(OBJECT_Actor,uActorID), a2, &a3, 0); v9 = &a3; } else v9 = pDir; v3->uYawAngle = LOWORD(v9->uYawAngle); v13 = pSpriteFrameTable->pSpriteSFrames[v3->pSpriteIDs[ANIM_AtkRanged]].uAnimLength; v3->uCurrentActionLength = 8 * v13; v3->uCurrentActionTime = 0; v3->uAIState = AttackingRanged4; Actor::PlaySound(uActorID, 0); pDira = pMonsterStats->pInfos[v3->pMonsterInfo.uID].uRecoveryTime; if (v3->pActorBuffs[ACTOR_BUFF_SLOWED].uExpireTime > 0) pDira *= 2; if ( pParty->bTurnBasedModeOn == 1 ) v3->pMonsterInfo.uRecoveryTime = pDira; else v3->pMonsterInfo.uRecoveryTime = v3->uCurrentActionLength + (int)(flt_6BE3A8_debug_recmod2 * pDira * 2.133333333333333); v3->vVelocity.z = 0; v3->vVelocity.y = 0; v3->vVelocity.x = 0; if ( ShouldMonsterPlayAttackAnim(v3->pMonsterInfo.uSpell2ID) ) { v3->uCurrentActionLength = 64; v3->uCurrentActionTime = 0; v3->uAIState = Fidgeting; v3->UpdateAnimation(); v3->uAIState = AttackingRanged4; } else v3->UpdateAnimation(); } else Actor::AI_Pursue1(uActorID, a2, uActorID, 64, pDir); } //----- (00403854) -------------------------------------------------------- void Actor::AI_SpellAttack1(unsigned int uActorID, signed int sTargetPid, AIDirection *pDir) { Actor *v3; // ebx@1 int16_t v4; // esi@3 int16_t v5; // edi@3 signed int v6; // eax@4 Vec3_int_ v7; // ST04_12@6 AIDirection *v9; // eax@8 __int16 v13; // ax@10 signed int v16; // ecx@17 AIDirection a3; // [sp+Ch] [bp-48h]@9 AIDirection v18; // [sp+28h] [bp-2Ch]@9 int v19; // [sp+44h] [bp-10h]@6 int v21; // [sp+4Ch] [bp-8h]@3 unsigned int pDira; // [sp+5Ch] [bp+8h]@10 v3 = &pActors[uActorID]; if ( PID_TYPE(sTargetPid) == OBJECT_Actor) { v6 = PID_ID(sTargetPid); v4 = pActors[v6].vPosition.x; v5 = pActors[v6].vPosition.y; v21 = (int)(pActors[v6].uActorHeight * 0.75 + pActors[v6].vPosition.z); } else if ( PID_TYPE(sTargetPid) == OBJECT_Player) { v4 = pParty->vPosition.x; v5 = pParty->vPosition.y; v21 = pParty->vPosition.z + pParty->sEyelevel; } else { Error("Should not get here"); return; } v19 = v3->uActorHeight; v7.z = v3->vPosition.z - (int)(v19 * -0.75); v7.y = v3->vPosition.y; v7.x = v3->vPosition.x; if ( sub_407A1C(v4, v5, v21, v7) ) { if ( pDir == nullptr ) { Actor::GetDirectionInfo(PID(OBJECT_Actor,uActorID), sTargetPid, &a3, 0); v9 = &a3; } else v9 = pDir; v3->uYawAngle = LOWORD(v9->uYawAngle); v13 = pSpriteFrameTable->pSpriteSFrames[v3->pSpriteIDs[ANIM_AtkRanged]].uAnimLength; v3->uCurrentActionLength = 8 * v13; v3->uCurrentActionTime = 0; v3->uAIState = AttackingRanged3; Actor::PlaySound(uActorID, 0); pDira = pMonsterStats->pInfos[v3->pMonsterInfo.uID].uRecoveryTime; if (v3->pActorBuffs[ACTOR_BUFF_SLOWED].uExpireTime > 0) pDira *= 2; if ( pParty->bTurnBasedModeOn == 1 ) v3->pMonsterInfo.uRecoveryTime = pDira; else v3->pMonsterInfo.uRecoveryTime = v3->uCurrentActionLength + (int)(flt_6BE3A8_debug_recmod2 * pDira * 2.133333333333333); v16 = v3->pMonsterInfo.uSpell1ID; v3->vVelocity.z = 0; v3->vVelocity.y = 0; v3->vVelocity.x = 0; if ( ShouldMonsterPlayAttackAnim(v3->pMonsterInfo.uSpell1ID) ) { v3->uCurrentActionLength = 64; v3->uCurrentActionTime = 0; v3->uAIState = Fidgeting; v3->UpdateAnimation(); v3->uAIState = AttackingRanged3; } else v3->UpdateAnimation(); } else Actor::AI_Pursue1(uActorID, sTargetPid, uActorID, 64, pDir); } //----- (0040368B) -------------------------------------------------------- void Actor::AI_MissileAttack2(unsigned int uActorID, signed int sTargetPid, AIDirection *pDir) { Actor *v3; // ebx@1 int16_t v4; // esi@3 int16_t v5; // edi@3 signed int v6; // eax@4 Vec3_int_ v7; // ST04_12@6 AIDirection *v9; // eax@8 __int16 v13; // ax@10 AIDirection a3; // [sp+Ch] [bp-48h]@9 AIDirection v17; // [sp+28h] [bp-2Ch]@9 int v18; // [sp+44h] [bp-10h]@6 int v20; // [sp+4Ch] [bp-8h]@3 unsigned int pDira; // [sp+5Ch] [bp+8h]@10 v3 = &pActors[uActorID]; if ( PID_TYPE(sTargetPid) == OBJECT_Actor) { v6 = PID_ID(sTargetPid); v4 = pActors[v6].vPosition.x; v5 = pActors[v6].vPosition.y; v20 = (int)(pActors[v6].uActorHeight * 0.75 + pActors[v6].vPosition.z); } else if ( PID_TYPE(sTargetPid) == OBJECT_Player) { v4 = pParty->vPosition.x; v5 = pParty->vPosition.y; v20 = pParty->vPosition.z + pParty->sEyelevel; } else { Error("Should not get here"); return; } v18 = v3->uActorHeight; v7.z = v3->vPosition.z - (int)(v18 * -0.75); v7.y = v3->vPosition.y; v7.x = v3->vPosition.x; if ( sub_407A1C(v4, v5, v20, v7) ) { if ( pDir == nullptr ) { Actor::GetDirectionInfo(PID(OBJECT_Actor,uActorID), sTargetPid, &a3, 0); v9 = &a3; } else v9 = pDir; v3->uYawAngle = LOWORD(v9->uYawAngle); v13 = pSpriteFrameTable->pSpriteSFrames[v3->pSpriteIDs[ANIM_AtkRanged]].uAnimLength; v3->uCurrentActionLength = 8 * v13; v3->uCurrentActionTime = 0; v3->uAIState = AttackingRanged2; Actor::PlaySound(uActorID, 0); pDira = pMonsterStats->pInfos[v3->pMonsterInfo.uID].uRecoveryTime; if ( v3->pActorBuffs[ACTOR_BUFF_SLOWED].uExpireTime > 0 ) pDira *= 2; if ( pParty->bTurnBasedModeOn != 1 ) v3->pMonsterInfo.uRecoveryTime = (int)(flt_6BE3A8_debug_recmod2 * pDira * 2.133333333333333); else v3->pMonsterInfo.uRecoveryTime = pDira; v3->vVelocity.z = 0; v3->vVelocity.y = 0; v3->vVelocity.x = 0; v3->UpdateAnimation(); } else Actor::AI_Pursue1(uActorID, sTargetPid, uActorID, 64, pDir); } //----- (00403476) -------------------------------------------------------- void Actor::AI_MissileAttack1(unsigned int uActorID, signed int sTargetPid, AIDirection *pDir) { Actor *v3; // ebx@1 int v4; // esi@3 int v5; // edi@3 signed int v6; // eax@4 Vec3_int_ v7; // ST04_12@6 AIDirection *v10; // eax@9 __int16 v14; // ax@11 AIDirection a3; // [sp+Ch] [bp-48h]@10 AIDirection v18; // [sp+28h] [bp-2Ch]@10 int v19; // [sp+44h] [bp-10h]@6 //signed int a2; // [sp+48h] [bp-Ch]@1 int v22; // [sp+50h] [bp-4h]@3 unsigned int pDira; // [sp+5Ch] [bp+8h]@11 v3 = &pActors[uActorID]; //a2 = edx0; if ( PID_TYPE(sTargetPid) == OBJECT_Actor) { v6 = PID_ID(sTargetPid); v4 = pActors[v6].vPosition.x; v5 = pActors[v6].vPosition.y; v22 = (int)(pActors[v6].uActorHeight * 0.75 + pActors[v6].vPosition.z); } else { if ( PID_TYPE(sTargetPid) == OBJECT_Player) { v4 = pParty->vPosition.x; v5 = pParty->vPosition.y; v22 = pParty->vPosition.z + pParty->sEyelevel; } else { v4 = (int)pDir; v5 = (int)pDir; } } v19 = v3->uActorHeight; v7.z = v3->vPosition.z - (unsigned int)(signed __int64)((double)v19 * -0.75); v7.y = v3->vPosition.y; v7.x = v3->vPosition.x; if ( sub_407A1C(v4, v5, v22, v7) || sub_407A1C(v7.x, v7.y, v7.z, Vec3_int_(v4, v5, v22))) { if ( pDir == nullptr ) { Actor::GetDirectionInfo(PID(OBJECT_Actor,uActorID), sTargetPid, &a3, 0); v10 = &a3; } else v10 = pDir; v3->uYawAngle = LOWORD(v10->uYawAngle); v14 = pSpriteFrameTable->pSpriteSFrames[v3->pSpriteIDs[ANIM_AtkRanged]].uAnimLength; v3->uCurrentActionLength = 8 * v14; v3->uCurrentActionTime = 0; v3->uAIState = AttackingRanged1; Actor::PlaySound(uActorID, 0); pDira = pMonsterStats->pInfos[v3->pMonsterInfo.uID].uRecoveryTime; if ( v3->pActorBuffs[ACTOR_BUFF_SLOWED].uExpireTime > 0 ) pDira *= 2; if ( pParty->bTurnBasedModeOn == 1 ) v3->pMonsterInfo.uRecoveryTime = pDira; else v3->pMonsterInfo.uRecoveryTime = v3->uCurrentActionLength - (int)(flt_6BE3A8_debug_recmod2 * pDira * -2.133333333333333); v3->vVelocity.z = 0; v3->vVelocity.y = 0; v3->vVelocity.x = 0; v3->UpdateAnimation(); } else Actor::AI_Pursue1(uActorID, sTargetPid, uActorID, 64, pDir); } //----- (004032B2) -------------------------------------------------------- void Actor::AI_RandomMove( unsigned int uActor_id, unsigned int uTarget_id, int radius, int uActionLength ) { int x; // ebx@1 int absy; // eax@1 unsigned int v9; // ebx@11 int v10; // ebx@13 AIDirection doNotInitializeBecauseShouldBeRandom; // [sp+Ch] [bp-30h]@7 int y; // [sp+30h] [bp-Ch]@1 int absx; // [sp+38h] [bp-4h]@1 x = pActors[uActor_id].vInitialPosition.x - pActors[uActor_id].vPosition.x; y = pActors[uActor_id].vInitialPosition.y - pActors[uActor_id].vPosition.y; absx = abs(x); absy = abs(y); if ( absx <= absy ) absx = absy + (absx / 2 ); else absx = absx + absy / 2; if ( MonsterStats::BelongsToSupertype(pActors[uActor_id].pMonsterInfo.uID, MONSTER_SUPERTYPE_TREANT) ) { if ( !uActionLength ) uActionLength = 256; Actor::AI_StandOrBored(uActor_id, OBJECT_Player, uActionLength, &doNotInitializeBecauseShouldBeRandom); return; } if ( pActors[uActor_id].pMonsterInfo.uMovementType == MONSTER_MOVEMENT_TYPE_GLOBAL && absx < 128 ) { Actor::AI_Stand(uActor_id, uTarget_id, 256, &doNotInitializeBecauseShouldBeRandom); return; } absx += ((rand() & 0xF) * radius) / 16; v9 = (stru_5C6E00->uIntegerDoublePi - 1) & stru_5C6E00->Atan2(x, y); if ( rand() % 100 < 25 ) { Actor::StandAwhile(uActor_id); return; } v10 = v9 + rand() % 256 - 128; if ( abs(v10 - pActors[uActor_id].uYawAngle) > 256 && !(pActors[uActor_id].uAttributes & ACTOR_ANIMATION) ) { Actor::AI_Stand(uActor_id, uTarget_id, 256, &doNotInitializeBecauseShouldBeRandom); return; } pActors[uActor_id].uYawAngle = v10; if ( pActors[uActor_id].uMovementSpeed) pActors[uActor_id].uCurrentActionLength = 32 * absx / pActors[uActor_id].uMovementSpeed; else pActors[uActor_id].uCurrentActionLength = 0; pActors[uActor_id].uCurrentActionTime = 0; pActors[uActor_id].uAIState = Tethered; if ( rand() % 100 < 2 ) Actor::PlaySound(uActor_id, 3); pActors[uActor_id].UpdateAnimation(); } //----- (004031C1) -------------------------------------------------------- char __fastcall Actor::_4031C1_update_job_never_gets_called(unsigned int uActorID, signed int a2, int a3) //attempted to implement something like jobs for actors, but apparently was never finished { return 0; /*unsigned int v3; // edi@1 Actor *v4; // esi@1 ActorJob *v5; // eax@1 signed int v6; // edx@2 ActorJob *v7; // eax@2 signed int v8; // edi@2 ActorJob *v9; // ecx@2 __int16 v10; // cx@15 signed int v12; // [sp+8h] [bp-4h]@1 v3 = uActorID; v12 = a2; v4 = &pActors[uActorID]; v5 = (ActorJob *)pActors[uActorID].CanAct(); if ( v5 ) { v6 = 65535; v7 = &v4->pScheduledJobs[v3]; v8 = 7; v9 = &v7[7];//(char *)&v7[7].uHour; while ( !(v9->uAttributes & 1) || v9->uHour > v12 ) { --v8; --v9; if ( v8 < 0 ) break; } if( v8 >= 0 ) v6 = v8; if ( !v8 && v6 == 65535 ) v6 = 7; v5 = &v7[v6]; if ( v4->vInitialPosition.x != v5->vPos.x || v4->vInitialPosition.y != v5->vPos.y || v4->vInitialPosition.z != v5->vPos.z || v4->pMonsterInfo.uMovementType != v5->uAction ) { v4->vInitialPosition.x = v5->vPos.x; v4->vInitialPosition.y = v5->vPos.y; v10 = v5->vPos.z; v4->vInitialPosition.z = v10; LOBYTE(v5) = v5->uAction; v4->pMonsterInfo.uMovementType = MONSTER_MOVEMENT_TYPE_STAIONARY; if ( a3 == 1 ) { v4->vPosition.x = v4->vInitialPosition.x; v4->vPosition.y = v4->vInitialPosition.y; LOBYTE(v5) = v10; v4->vPosition.z = v10; } } } return (char)v5;*/ } //----- (004030AD) -------------------------------------------------------- void Actor::AI_Stun(unsigned int uActorID, signed int edx0, int stunRegardlessOfState) { __int16 v7; // ax@16 AIDirection a3; // [sp+Ch] [bp-40h]@16 if ( pActors[uActorID].uAIState == Fleeing ) pActors[uActorID].uAttributes |= ACTOR_FLEEING; if ( pActors[uActorID].pMonsterInfo.uHostilityType != 4 ) { pActors[uActorID].uAttributes &= 0xFFFFFFFB;//~0x4 pActors[uActorID].pMonsterInfo.uHostilityType = MonsterInfo::Hostility_Long; } if ( pActors[uActorID].pActorBuffs[ACTOR_BUFF_CHARM].uExpireTime > 0 ) pActors[uActorID].pActorBuffs[ACTOR_BUFF_CHARM].Reset(); if ( pActors[uActorID].pActorBuffs[ACTOR_BUFF_AFRAID].uExpireTime > 0 ) pActors[uActorID].pActorBuffs[ACTOR_BUFF_AFRAID].Reset(); if ( stunRegardlessOfState || (pActors[uActorID].uAIState != Stunned && pActors[uActorID].uAIState != AttackingRanged1 && pActors[uActorID].uAIState != AttackingRanged2 && pActors[uActorID].uAIState != AttackingRanged3 && pActors[uActorID].uAIState != AttackingRanged4 && pActors[uActorID].uAIState != AttackingMelee)) { Actor::GetDirectionInfo(PID(OBJECT_Actor,uActorID), edx0, &a3, 0); //v10 = &a3; pActors[uActorID].uYawAngle = LOWORD(a3.uYawAngle); v7 = pSpriteFrameTable->pSpriteSFrames[pActors[uActorID].pSpriteIDs[ANIM_GotHit]].uAnimLength; pActors[uActorID].uCurrentActionTime = 0; pActors[uActorID].uAIState = Stunned; pActors[uActorID].uCurrentActionLength = 8 * v7; Actor::PlaySound(uActorID, 2); pActors[uActorID].UpdateAnimation(); } } //----- (00402F87) -------------------------------------------------------- void Actor::AI_Bored(unsigned int uActorID, unsigned int uObjID, AIDirection *a4) { unsigned int v7; // eax@3 unsigned int v9; // eax@3 Actor* actor = &pActors[uActorID]; AIDirection a3; // [sp+Ch] [bp-5Ch]@2 if (!a4) { Actor::GetDirectionInfo(PID(OBJECT_Actor,uActorID), uObjID, &a3, 0); a4 = &a3; } actor->uCurrentActionLength = 8 * pSpriteFrameTable->pSpriteSFrames[actor->pSpriteIDs[ANIM_Bored]].uAnimLength; v7 = stru_5C6E00->Atan2(actor->vPosition.x - pEngine->pIndoorCameraD3D->vPartyPos.x, actor->vPosition.y - pEngine->pIndoorCameraD3D->vPartyPos.y); v9 = stru_5C6E00->uIntegerPi + actor->uYawAngle + ((signed int)stru_5C6E00->uIntegerPi >> 3) - v7; if ( v9 & 0x700 ) // turned away - just stand Actor::AI_Stand(uActorID, uObjID, actor->uCurrentActionLength, a4); else // facing player - play bored anim { actor->uAIState = Fidgeting; actor->uCurrentActionTime = 0; actor->uYawAngle = a4->uYawAngle; actor->vVelocity.z = 0; actor->vVelocity.y = 0; actor->vVelocity.x = 0; if ( rand() % 100 < 5 ) Actor::PlaySound(uActorID, 3); actor->UpdateAnimation(); } } //----- (00402F27) -------------------------------------------------------- void Actor::Resurrect(unsigned int uActorID) { Actor *pActor; // esi@1 pActor = &pActors[uActorID]; pActor->uCurrentActionTime = 0; pActor->uAIState = Resurrected; pActor->uCurrentActionAnimation = ANIM_Dying; pActor->uCurrentActionLength = 8 * pSpriteFrameTable->pSpriteSFrames[pActor->pSpriteIDs[ANIM_Dying]].uAnimLength; pActor->sCurrentHP = LOWORD(pActor->pMonsterInfo.uHP); Actor::PlaySound(uActorID, 1); pActor->UpdateAnimation(); } //----- (00402D6E) -------------------------------------------------------- void Actor::Die(unsigned int uActorID) { Actor* actor = &pActors[uActorID]; actor->uCurrentActionTime = 0; actor->uAIState = Dying; actor->uCurrentActionAnimation = ANIM_Dying; actor->sCurrentHP = 0; actor->uCurrentActionLength = 8 * pSpriteFrameTable->pSpriteSFrames[actor->pSpriteIDs[ANIM_Dying]].uAnimLength; actor->pActorBuffs[ACTOR_BUFF_PARALYZED].Reset(); actor->pActorBuffs[ACTOR_BUFF_STONED].Reset(); Actor::PlaySound(uActorID, 1); actor->UpdateAnimation(); for (uint i = 0; i < 5; ++i) if (pParty->monster_id_for_hunting[i] == actor->pMonsterInfo.uID) pParty->monster_for_hunting_killed[i] = true; for (uint i = 0; i < 22; ++i) actor->pActorBuffs[i].Reset(); ItemGen drop; drop.Reset(); switch (actor->pMonsterInfo.uID) { case MONSTER_HARPY_1: case MONSTER_HARPY_2: case MONSTER_HARPY_3: drop.uItemID = ITEM_HARPY_FEATHER; break; case MONSTER_OOZE_1: case MONSTER_OOZE_2: case MONSTER_OOZE_3: drop.uItemID = ITEM_OOZE_ECTOPLASM_BOTTLE; break; case MONSTER_TROLL_1: case MONSTER_TROLL_2: case MONSTER_TROLL_3: drop.uItemID = ITEM_TROLL_BLOOD; break; case MONSTER_DEVIL_1: case MONSTER_DEVIL_2: case MONSTER_DEVIL_3: drop.uItemID = ITEM_DEVIL_ICHOR; break; case MONSTER_DRAGON_1: case MONSTER_DRAGON_2: case MONSTER_DRAGON_3: drop.uItemID = ITEM_DRAGON_EYE; break; } if (rand() % 100 < 20 && drop.uItemID != 0) { SpriteObject::sub_42F7EB_DropItemAt(pItemsTable->pItems[drop.uItemID].uSpriteID, actor->vPosition.x, actor->vPosition.y, actor->vPosition.z + 16, rand() % 200 + 200, 1, 1, 0, &drop); } if (actor->pMonsterInfo.uSpecialAbilityType == MONSTER_SPECIAL_ABILITY_EXPLODE) Actor::Explode(uActorID); } //----- (00402CED) -------------------------------------------------------- void Actor::PlaySound(unsigned int uActorID, unsigned int uSoundID) { unsigned __int16 v3; // dx@1 v3 = pActors[uActorID].pSoundSampleIDs[uSoundID]; if ( v3 ) { if ( pActors[uActorID].pActorBuffs[ACTOR_BUFF_SHRINK].uExpireTime <= 0 ) pAudioPlayer->PlaySound((SoundID)v3, PID(OBJECT_Actor, uActorID), 0, -1, 0, 0, 0, 0); else { switch(pActors[uActorID].pActorBuffs[ACTOR_BUFF_SHRINK].uPower) { case 1: pAudioPlayer->PlaySound((SoundID)v3, PID(OBJECT_Actor, uActorID), 0, 0, 0, 0, 0, 33075); break; case 2: pAudioPlayer->PlaySound((SoundID)v3, PID(OBJECT_Actor, uActorID), 0, 0, 0, 0, 0, 33075); break; case 3: case 4: pAudioPlayer->PlaySound((SoundID)v3, PID(OBJECT_Actor, uActorID), 0, 0, 0, 0, 0, 33075); break; default: pAudioPlayer->PlaySound((SoundID)v3, PID(OBJECT_Actor, uActorID), 0, -1, 0, 0, 0, 0); break; } } } } //----- (00402AD7) -------------------------------------------------------- void Actor::AI_Pursue1(unsigned int uActorID, unsigned int a2, signed int arg0, signed int uActionLength, AIDirection *pDir) { int v6; // eax@1 Actor *v7; // ebx@1 unsigned int v8; // ecx@1 AIDirection *v10; // esi@6 AIDirection a3; // [sp+Ch] [bp-5Ch]@7 unsigned int v18; // [sp+64h] [bp-4h]@1 v6 = 0; v7 = &pActors[uActorID]; v8 = PID(OBJECT_Actor,uActorID); if ( v7->pMonsterInfo.uFlying != 0 && !pParty->bFlying ) //TODO: Does v6 have a point? { if ( v7->pMonsterInfo.uMissleAttack1Type ) v6 = v7->uActorRadius + 512; else v6 = pParty->uPartyHeight; } if ( pDir == nullptr ) { Actor::GetDirectionInfo(v8, a2, &a3, v6); v10 = &a3; } else v10 = pDir; if ( MonsterStats::BelongsToSupertype(v7->pMonsterInfo.uID, MONSTER_SUPERTYPE_TREANT) ) { if ( !uActionLength ) uActionLength = 256; Actor::AI_StandOrBored(uActorID, 4, uActionLength, v10); return; } if ( v10->uDistance < 307.2 ) { if ( !uActionLength ) uActionLength = 256; Actor::AI_Stand(uActorID, a2, uActionLength, v10); return; } if ( v7->uMovementSpeed == 0 ) { Actor::AI_Stand(uActorID, a2, uActionLength, v10); return; } if ( arg0 % 2 ) v18 = -16; else v18 = 16; v7->uYawAngle = stru_5C6E00->Atan2( pParty->vPosition.x + (int)fixpoint_mul(stru_5C6E00->Cos(v18 + stru_5C6E00->uIntegerPi + v10->uYawAngle), v10->uDistanceXZ) - v7->vPosition.x, pParty->vPosition.y + (int)fixpoint_mul(stru_5C6E00->Sin(v18 + stru_5C6E00->uIntegerPi + v10->uYawAngle), v10->uDistanceXZ) - v7->vPosition.y); if ( uActionLength ) v7->uCurrentActionLength = uActionLength; else v7->uCurrentActionLength = 128; v7->uPitchAngle = LOWORD(v10->uPitchAngle); v7->uAIState = Pursuing; v7->UpdateAnimation(); } //----- (00402968) -------------------------------------------------------- void Actor::AI_Flee(unsigned int uActorID, signed int sTargetPid, int uActionLength, AIDirection *a4) { Actor *v5; // ebx@1 int v7; // ecx@2 unsigned __int16 v9; // ax@15 AIDirection v10; // [sp+8h] [bp-7Ch]@4 AIDirection a3; // [sp+24h] [bp-60h]@3 AIDirection* v13; // [sp+5Ch] [bp-28h]@4 v5 = &pActors[uActorID]; if ( v5->CanAct() ) { v7 = PID(OBJECT_Actor,uActorID); if ( !a4 ) { Actor::GetDirectionInfo(v7, sTargetPid, &a3, v5->pMonsterInfo.uFlying); a4 = &a3; } Actor::GetDirectionInfo(v7, 4u, &v10, 0); v13 = &v10; if ( MonsterStats::BelongsToSupertype(v5->pMonsterInfo.uID, MONSTER_SUPERTYPE_TREANT) || PID_TYPE(sTargetPid) == OBJECT_Actor && v13->uDistance < 307.2 ) { if ( !uActionLength ) uActionLength = 256; Actor::AI_StandOrBored(uActorID, 4, uActionLength, v13); } else { if ( v5->uMovementSpeed ) v5->uCurrentActionLength = (signed int)(a4->uDistanceXZ << 7) / v5->uMovementSpeed; else v5->uCurrentActionLength = 0; if ( v5->uCurrentActionLength > 256 ) v5->uCurrentActionLength = 256; v5->uYawAngle = LOWORD(stru_5C6E00->uIntegerHalfPi) + LOWORD(a4->uYawAngle); v5->uYawAngle = LOWORD(stru_5C6E00->uDoublePiMask) & (v5->uYawAngle + rand() % (signed int)stru_5C6E00->uIntegerPi); v9 = LOWORD(a4->uPitchAngle); v5->uCurrentActionTime = 0; v5->uPitchAngle = v9; v5->uAIState = Fleeing; v5->UpdateAnimation(); } } } //----- (0040281C) -------------------------------------------------------- void Actor::AI_Pursue2(unsigned int uActorID, unsigned int a2, signed int uActionLength, AIDirection *pDir, int a5) { int v6; // eax@1 Actor *v7; // ebx@1 unsigned int v8; // ecx@1 AIDirection *v10; // esi@7 signed __int16 v13; // cx@19 unsigned __int16 v14; // ax@25 AIDirection a3; // [sp+Ch] [bp-40h]@8 AIDirection v18; // [sp+28h] [bp-24h]@8 v6 = 0; v7 = &pActors[uActorID]; v8 = PID(OBJECT_Actor,uActorID); if ( v7->pMonsterInfo.uFlying != 0 && !pParty->bFlying ) { if ( v7->pMonsterInfo.uMissleAttack1Type && uCurrentlyLoadedLevelType == LEVEL_Outdoor ) v6 = v7->uActorRadius + 512; else v6 = pParty->uPartyHeight; } v10 = pDir; if ( !pDir ) { Actor::GetDirectionInfo(v8, a2, &a3, v6); v10 = &a3; } if ( MonsterStats::BelongsToSupertype(v7->pMonsterInfo.uID, MONSTER_SUPERTYPE_TREANT) ) { if ( !uActionLength ) uActionLength = 256; Actor::AI_StandOrBored(uActorID, 4, uActionLength, v10); return; } if ( (signed int)v10->uDistance < a5 ) { if ( !uActionLength ) uActionLength = 256; Actor::AI_StandOrBored(uActorID, a2, uActionLength, v10); return; } if ( uActionLength ) { v7->uCurrentActionLength = uActionLength; } else { v13 = v7->uMovementSpeed; if ( v13 ) v7->uCurrentActionLength = (signed int)(v10->uDistanceXZ << 7) / v13; else v7->uCurrentActionLength = 0; if ( v7->uCurrentActionLength > 32 ) v7->uCurrentActionLength = 32; } v7->uYawAngle = LOWORD(v10->uYawAngle); v14 = LOWORD(v10->uPitchAngle); v7->uCurrentActionTime = 0; v7->uPitchAngle = v14; v7->uAIState = Pursuing; v7->UpdateAnimation(); } //----- (00402686) -------------------------------------------------------- void Actor::AI_Pursue3(unsigned int uActorID, unsigned int a2, signed int uActionLength, AIDirection *a4) { int v5; // eax@1 Actor *v6; // ebx@1 int v7; // ecx@1 signed __int16 v12; // cx@19 __int16 v14; // ax@25 unsigned __int16 v16; // ax@28 AIDirection a3; // [sp+Ch] [bp-40h]@8 AIDirection* v20; // [sp+28h] [bp-24h]@8 v5 = 0; v6 = &pActors[uActorID]; v7 = PID(OBJECT_Actor,uActorID); if ( v6->pMonsterInfo.uFlying != 0 && !pParty->bFlying ) { if ( v6->pMonsterInfo.uMissleAttack1Type && uCurrentlyLoadedLevelType == LEVEL_Outdoor ) v5 = v6->uActorRadius + 512; else v5 = pParty->uPartyHeight; } if ( !a4 ) { Actor::GetDirectionInfo(v7, a2, &a3, v5); v20 = &a3; } if ( MonsterStats::BelongsToSupertype(v6->pMonsterInfo.uID, MONSTER_SUPERTYPE_TREANT) ) { if ( !uActionLength ) uActionLength = 256; return Actor::AI_StandOrBored(uActorID, 4, uActionLength, a4); } if ( a4->uDistance < 307.2 ) { if ( !uActionLength ) uActionLength = 256; return Actor::AI_StandOrBored(uActorID, a2, uActionLength, a4); } if ( uActionLength ) v6->uCurrentActionLength = uActionLength + rand() % uActionLength; else { v12 = v6->uMovementSpeed; if ( v12 ) v6->uCurrentActionLength = (signed int)(a4->uDistanceXZ << 7) / v12; else v6->uCurrentActionLength = 0; if ( v6->uCurrentActionLength > 128 ) v6->uCurrentActionLength = 128; } v14 = LOWORD(a4->uYawAngle); if ( rand() % 2 ) v14 += 256; else v14 -= 256; v6->uYawAngle = v14; v16 = LOWORD(a4->uPitchAngle); v6->uCurrentActionTime = 0; v6->uPitchAngle = v16; v6->uAIState = Pursuing; if ( rand() % 100 < 2 ) Actor::PlaySound(uActorID, 2); v6->UpdateAnimation(); } //----- (00401221) -------------------------------------------------------- void Actor::_SelectTarget(unsigned int uActorID, int *a2, bool can_target_party) { int v5; // ecx@1 signed int v10; // eax@13 uint v11; // ebx@16 uint v12; // eax@16 signed int v14; // eax@31 uint v15; // edi@43 uint v16; // ebx@45 uint v17; // eax@45 signed int closestId; // [sp+14h] [bp-1Ch]@1 uint v23; // [sp+1Ch] [bp-14h]@16 unsigned int lowestRadius; // [sp+24h] [bp-Ch]@1 uint v27; // [sp+2Ch] [bp-4h]@16 uint v28; // [sp+2Ch] [bp-4h]@45 lowestRadius = UINT_MAX; v5 = 0; *a2 = 0; closestId = 0; assert(uActorID < uNumActors); Actor* thisActor = &pActors[uActorID]; for (uint i = 0; i < uNumActors; ++i) { Actor* actor = &pActors[i]; if (actor->uAIState == Dead || actor->uAIState == Dying || actor->uAIState == Removed || actor->uAIState == Summoned || actor->uAIState == Disabled || uActorID == i ) continue; if (thisActor->uLastCharacterIDToHit == 0 || PID(OBJECT_Actor,v5) != thisActor->uLastCharacterIDToHit ) { v10 = thisActor->GetActorsRelation(actor); if ( v10 == 0 ) continue; } else if (thisActor->IsNotAlive()) { thisActor->uLastCharacterIDToHit = 0; v10 = thisActor->GetActorsRelation(actor); if ( v10 == 0 ) continue; } else { if ( (actor->uGroup != 0 || thisActor->uGroup != 0) && actor->uGroup == thisActor->uGroup ) continue; v10 = 4; } if ( thisActor->pMonsterInfo.uHostilityType ) v10 = pMonsterStats->pInfos[thisActor->pMonsterInfo.uID].uHostilityType; v11 = _4DF380_hostilityRanges[v10]; v23 = abs(thisActor->vPosition.x - actor->vPosition.x); v27 = abs(thisActor->vPosition.y - actor->vPosition.y); v12 = abs(thisActor->vPosition.z - actor->vPosition.z); if ( v23 <= v11 && v27 <= v11 && v12 <= v11 && sub_4070EF_prolly_detect_player(PID(OBJECT_Actor, i), PID(OBJECT_Actor, uActorID)) && v23 * v23 + v27 * v27 + v12 * v12 < lowestRadius ) { lowestRadius = v23 * v23 + v27 * v27 + v12 * v12; closestId = i; } } if ( lowestRadius != UINT_MAX ) { *a2 = PID(OBJECT_Actor, closestId); } if (can_target_party && !pParty->Invisible()) { if ( thisActor->ActorEnemy() && thisActor->pActorBuffs[ACTOR_BUFF_ENSLAVED].uExpireTime <= 0 && thisActor->pActorBuffs[ACTOR_BUFF_CHARM].uExpireTime <= 0 && thisActor->pActorBuffs[ACTOR_BUFF_SUMMONED].uExpireTime <= 0 ) v14 = 4; else v14 = thisActor->GetActorsRelation(0); if ( v14 != 0 ) { if ( !thisActor->pMonsterInfo.uHostilityType ) v15 = _4DF380_hostilityRanges[v14]; else v15 = _4DF380_hostilityRanges[4]; v16 = abs(thisActor->vPosition.x - pParty->vPosition.x); v28 = abs(thisActor->vPosition.y - pParty->vPosition.y); v17 = abs(thisActor->vPosition.z - pParty->vPosition.z); if ( v16 <= v15 && v28 <= v15 && v17 <= v15 && (v16 * v16 + v28 * v28 + v17 * v17 < lowestRadius)) { *a2 = OBJECT_Player; } } } } // 4DF380: using guessed type int dword_4DF380[]; // 4DF390: using guessed type int dword_4DF390; //----- (0040104C) -------------------------------------------------------- signed int Actor::GetActorsRelation(Actor *otherActPtr) { unsigned int thisGroup; // ebp@19 int otherGroup; // eax@22 unsigned int thisAlly; // edx@25 unsigned int otherAlly; // edx@33 if ( otherActPtr) { if ( otherActPtr->uGroup != 0 && this->uGroup != 0 && otherActPtr->uGroup == this->uGroup ) return 0; } if (this->pActorBuffs[ACTOR_BUFF_BERSERK].uExpireTime > 0) return 4; thisAlly = this->uAlly; if ( this->pActorBuffs[ACTOR_BUFF_ENSLAVED].uExpireTime > 0 || thisAlly == 9999) thisGroup = 0; else if ( thisAlly > 0 ) thisGroup = thisAlly; else thisGroup = (this->pMonsterInfo.uID - 1) / 3 + 1; if ( otherActPtr ) { if (otherActPtr->pActorBuffs[ACTOR_BUFF_BERSERK].uExpireTime > 0) return 4; otherAlly = otherActPtr->uAlly; if ( otherActPtr->pActorBuffs[ACTOR_BUFF_ENSLAVED].uExpireTime > 0 || otherAlly == 9999) otherGroup = 0; else if ( otherAlly > 0 ) otherGroup = otherAlly; else otherGroup = (otherActPtr->pMonsterInfo.uID - 1) / 3 + 1; } else otherGroup = 0; if ( this->pActorBuffs[ACTOR_BUFF_CHARM].uExpireTime > 0 && !otherGroup || otherActPtr && otherActPtr->pActorBuffs[ACTOR_BUFF_CHARM].uExpireTime > 0 && !thisGroup ) return 0; if ( this->pActorBuffs[ACTOR_BUFF_ENSLAVED].uExpireTime <= 0 && this->ActorEnemy() && !otherGroup ) return 4; if (thisGroup >= 89 || otherGroup >= 89) return 0; if ( thisGroup == 0 ) { if ( (!otherActPtr || this->pActorBuffs[ACTOR_BUFF_ENSLAVED].uExpireTime > 0 && otherActPtr->ActorFriend()) && !pFactionTable->relations[otherGroup][0]) return pFactionTable->relations[0][otherGroup]; else return 4; } else return pFactionTable->relations[thisGroup][otherGroup]; } //----- (0045976D) -------------------------------------------------------- void Actor::UpdateAnimation() { ResetAnimation(); switch (uAIState) { case Tethered: uCurrentActionAnimation = ANIM_Walking; break; case AttackingMelee: uCurrentActionAnimation = ANIM_AtkMelee; uAttributes |= ACTOR_ANIMATION; break; case AttackingRanged1: case AttackingRanged2: case AttackingRanged3: case AttackingRanged4: uCurrentActionAnimation = ANIM_AtkRanged; uAttributes |= ACTOR_ANIMATION; break; case Dying: case Resurrected: uCurrentActionAnimation = ANIM_Dying; uAttributes |= ACTOR_ANIMATION; break; case Pursuing: case Fleeing: uCurrentActionAnimation = ANIM_Walking; uAttributes |= ACTOR_ANIMATION; break; case Stunned: uCurrentActionAnimation = ANIM_GotHit; uAttributes |= ACTOR_ANIMATION; break; case Fidgeting: uCurrentActionAnimation = ANIM_Bored; uAttributes |= ACTOR_ANIMATION; break; case Standing: case Interacting: case Summoned: uCurrentActionAnimation = ANIM_Standing; uAttributes |= ACTOR_ANIMATION; break; case Dead: if (pSpriteFrameTable->pSpriteSFrames[pSpriteIDs[ANIM_Dead]].pHwSpriteIDs[0] <= 0) uAIState = Removed; else uCurrentActionAnimation = ANIM_Dead; break; case Removed: case Disabled: return; default: assert(false); } } //----- (00459671) -------------------------------------------------------- void Actor::Reset() { this->pActorName[0] = 0; this->word_000086_some_monster_id = 0; this->sNPC_ID = 0; this->vPosition.z = 0; this->vPosition.y = 0; this->vPosition.x = 0; this->vVelocity.z = 0; this->vVelocity.y = 0; this->vVelocity.x = 0; this->uYawAngle = 0; this->uPitchAngle = 0; this->uAttributes = 0; this->uSectorID = 0; this->uCurrentActionTime = 0; this->vInitialPosition.z = 0; this->vInitialPosition.y = 0; this->vInitialPosition.x = 0; this->vGuardingPosition.z = 0; this->vGuardingPosition.y = 0; this->vGuardingPosition.x = 0; this->uTetherDistance = 256; this->uActorRadius = 32; this->uActorHeight = 128; this->uAIState = Standing; this->uCurrentActionAnimation = ANIM_Standing; this->uMovementSpeed = 200; this->uCarriedItemID = 0; this->uGroup = 0; this->uAlly = 0; this->uSummonerID = 0; this->uLastCharacterIDToHit = 0; this->dword_000334_unique_name = 0; memset(this->pSpriteIDs, 0, sizeof(pSpriteIDs)); memset(this->pActorBuffs, 0, 0x160u); } //----- (0045959A) -------------------------------------------------------- void Actor::PrepareSprites(char load_sounds_if_bit1_set) { MonsterDesc *v3; // esi@1 MonsterInfo *v9; // [sp+84h] [bp-10h]@1 v3 = &pMonsterList->pMonsters[pMonsterInfo.uID - 1]; v9 = &pMonsterStats->pInfos[pMonsterInfo.uID - 1 + 1]; //v12 = pSpriteIDs; //Source = (char *)v3->pSpriteNames; //do for (uint i = 0; i < 8; ++i) { //strcpy(pSpriteName, v3->pSpriteNames[i]); pSpriteIDs[i] = pSpriteFrameTable->FastFindSprite(v3->pSpriteNames[i]); pSpriteFrameTable->InitializeSprite(pSpriteIDs[i]); } uActorHeight = v3->uMonsterHeight; uActorRadius = v3->uMonsterRadius; uMovementSpeed = v9->uBaseSpeed; if ( !(load_sounds_if_bit1_set & 1) ) { for ( int i = 0; i < 4; ++i ) pSoundSampleIDs[i] = v3->pSoundSampleIDs[i]; } } //----- (00459667) -------------------------------------------------------- void Actor::Remove() { this->uAIState = Removed; } //----- (0043B1B0) -------------------------------------------------------- void Actor::ActorDamageFromMonster(signed int attacker_id, unsigned int actor_id, Vec3_int_ *pVelocity, signed int a4) { int v4; // ebx@1 int dmgToRecv; // qax@8 signed int v12; // ecx@20 int finalDmg; // edi@30 int pushDistance; // [sp+20h] [bp+Ch]@34 v4 = 0; if ( PID_TYPE(attacker_id) == OBJECT_Item) { v4 = pSpriteObjects[PID_ID(attacker_id)].field_60_distance_related_prolly_lod; attacker_id = pSpriteObjects[PID_ID(attacker_id)].spell_caster_pid; } if ( PID_TYPE(attacker_id) == OBJECT_Actor) { if ( !pActors[actor_id].IsNotAlive() ) { pActors[actor_id].uLastCharacterIDToHit = attacker_id; if ( pActors[actor_id].uAIState == Fleeing ) pActors[actor_id].uAttributes |= ACTOR_FLEEING; if ( pActors[PID_ID(attacker_id)]._4273BB_DoesHitOtherActor(&pActors[actor_id], v4, 0) ) { dmgToRecv = pActors[PID_ID(attacker_id)]._43B3E0_CalcDamage(a4); if ( pActors[PID_ID(attacker_id)].pActorBuffs[ACTOR_BUFF_SHRINK].uExpireTime > 0 ) { if ( pActors[PID_ID(attacker_id)].pActorBuffs[ACTOR_BUFF_SHRINK].uPower ) dmgToRecv = dmgToRecv / pActors[PID_ID(attacker_id)].pActorBuffs[ACTOR_BUFF_SHRINK].uPower; } if ( pActors[actor_id].pActorBuffs[ACTOR_BUFF_STONED].uExpireTime > 0 ) dmgToRecv = 0; if ( a4 == 0 ) v12 = pActors[PID_ID(attacker_id)].pMonsterInfo.uAttack1Type; else if ( a4 == 1 ) { v12 = pActors[PID_ID(attacker_id)].pMonsterInfo.uAttack2Type; if ( SHIDWORD(pActors[actor_id].pActorBuffs[ACTOR_BUFF_SHIELD].uExpireTime) > 0 ) dmgToRecv = dmgToRecv / 2; } else if ( a4 == 2 ) v12 = pSpellStats->pInfos[pActors[actor_id].pMonsterInfo.uSpell1ID].uSchool; else if ( a4 == 3 ) v12 = pSpellStats->pInfos[pActors[actor_id].pMonsterInfo.uSpell2ID].uSchool; else if ( a4 == 4 ) v12 = pActors[PID_ID(attacker_id)].pMonsterInfo.field_3C_some_special_attack; else v12 = 4; finalDmg = pActors[actor_id].CalcMagicalDamageToActor((DAMAGE_TYPE)v12, dmgToRecv); pActors[actor_id].sCurrentHP -= finalDmg; if ( finalDmg ) { if ( pActors[actor_id].sCurrentHP > 0 ) Actor::AI_Stun(actor_id, attacker_id, 0); else Actor::Die(actor_id); Actor::AggroSurroundingPeasants(actor_id, 0); pushDistance = 20 * finalDmg / pActors[actor_id].pMonsterInfo.uHP; if ( pushDistance > 10 ) pushDistance = 10; if ( !MonsterStats::BelongsToSupertype(pActors[actor_id].pMonsterInfo.uID, MONSTER_SUPERTYPE_TREANT) ) { pVelocity->x = (int32)fixpoint_mul(pushDistance, pVelocity->x); pVelocity->y = (int32)fixpoint_mul(pushDistance, pVelocity->y); pVelocity->z = (int32)fixpoint_mul(pushDistance, pVelocity->z); pActors[actor_id].vVelocity.x = 50 * LOWORD(pVelocity->x); pActors[actor_id].vVelocity.y = 50 * LOWORD(pVelocity->y); pActors[actor_id].vVelocity.z = 50 * LOWORD(pVelocity->z); } Actor::AddBloodsplatOnDamageOverlay(actor_id, 1, finalDmg); } else Actor::AI_Stun(actor_id, attacker_id, 0); return; } } } } //----- (0044FD29) -------------------------------------------------------- void Actor::SummonMinion( int summonerId ) { unsigned __int8 extraSummonLevel; // al@1 int summonMonsterBaseType; // esi@1 int v5; // edx@2 int v7; // edi@10 Actor *actor; // esi@10 MonsterInfo *v9; // ebx@10 //MonsterDesc *v10; // edi@10 int v13; // ebx@10 int v15; // edi@10 int v17; // ebx@10 unsigned int v19; // qax@10 int result; // eax@13 unsigned int monsterId; // [sp+10h] [bp-18h]@8 int v27; // [sp+18h] [bp-10h]@10 int actorSector; // [sp+1Ch] [bp-Ch]@8 actorSector = 0; if ( uCurrentlyLoadedLevelType == LEVEL_Indoor ) actorSector = pIndoor->GetSector(this->vPosition.x, this->vPosition.y, this->vPosition.z); v19 = this->uAlly; if ( !this->uAlly ) { monsterId = this->pMonsterInfo.uID - 1; v19 = (uint)(monsterId * 0.33333334); } v27 = uCurrentlyLoadedLevelType == LEVEL_Outdoor ? 128 : 64; v13 = rand() % 2048; v15 = fixpoint_mul(stru_5C6E00->Cos(v13), v27) + this->vPosition.x; v17 = fixpoint_mul(stru_5C6E00->Sin(v13), v27) + this->vPosition.y; if (uCurrentlyLoadedLevelType == LEVEL_Indoor) { result = pIndoor->GetSector(v15, v17, this->vPosition.z); if (result != actorSector) return; result = BLV_GetFloorLevel(v15, v17, v27, result, &monsterId); if (result != -30000) return; if (abs(result - v27) > 1024) return; } extraSummonLevel = this->pMonsterInfo.uSpecialAbilityDamageDiceRolls; summonMonsterBaseType = this->pMonsterInfo.field_3C_some_special_attack; if ( extraSummonLevel ) { if ( extraSummonLevel >= 1 && extraSummonLevel <= 3 ) summonMonsterBaseType = summonMonsterBaseType + extraSummonLevel - 1; } else { v5 = rand() % 100; if ( v5 >= 90 ) summonMonsterBaseType += 2; else if ( v5 >= 60 ) summonMonsterBaseType += 1; } v7 = summonMonsterBaseType - 1; actor = &pActors[uNumActors]; v9 = &pMonsterStats->pInfos[v7 + 1]; pActors[uNumActors].Reset(); strcpy(actor->pActorName, v9->pName); actor->sCurrentHP = LOWORD(v9->uHP); memcpy(&actor->pMonsterInfo, v9, sizeof(actor->pMonsterInfo)); actor->word_000086_some_monster_id = summonMonsterBaseType; actor->uActorRadius = pMonsterList->pMonsters[v7].uMonsterRadius; actor->uActorHeight = pMonsterList->pMonsters[v7].uMonsterHeight; actor->pMonsterInfo.uTreasureDiceRolls = 0; actor->pMonsterInfo.uTreasureType = 0; actor->pMonsterInfo.uExp = 0; actor->uMovementSpeed = pMonsterList->pMonsters[v7].uMovementSpeed; actor->vInitialPosition.x = v15; actor->vInitialPosition.y = v17; actor->vInitialPosition.z = this->vPosition.z; actor->vPosition.x = v15; actor->vPosition.y = v17; actor->vPosition.z = this->vPosition.z; actor->uTetherDistance = 256; actor->uSectorID = actorSector; actor->PrepareSprites(0); actor->pMonsterInfo.uHostilityType = MonsterInfo::Hostility_Friendly; actor->uAlly = v19; actor->uCurrentActionTime = 0; actor->uGroup = this->uGroup; actor->uAIState = Summoned; actor->uCurrentActionLength = 256; actor->UpdateAnimation(); ++uNumActors; ++this->pMonsterInfo.uSpecialAbilityDamageDiceBonus; if ( ActorEnemy()) actor->uAttributes |= ACTOR_AGGRESSOR; actor->uSummonerID = PID(OBJECT_Actor,summonerId); } // 46DF1A: using guessed type int __fastcall 46DF1A_collide_against_actor(int, int); //----- (0046DF1A) -------------------------------------------------------- bool Actor::_46DF1A_collide_against_actor( int a1, int a2 ) { Actor *v2; // edi@1 unsigned __int16 v3; // ax@1 int v4; // esi@6 int v8; // ecx@14 int v9; // eax@14 int v10; // ebx@14 int v11; // esi@14 int v12; // ebx@15 int v13; // ebx@17 v2 = &pActors[a1]; v3 = v2->uAIState; if ( v3 == Removed || v3 == Dying || v3 == Disabled || v3 == Dead || v3 == Summoned ) return 0; v4 = v2->uActorRadius; if ( a2 ) v4 = a2; if (stru_721530.sMaxX > v2->vPosition.x + v4 || stru_721530.sMinX < v2->vPosition.x - v4 || stru_721530.sMaxY > v2->vPosition.y + v4 || stru_721530.sMinY < v2->vPosition.y - v4 || stru_721530.sMaxZ > v2->vPosition.z + v2->uActorHeight || stru_721530.sMinZ < v2->vPosition.z) { return false; } v8 = v2->vPosition.x - stru_721530.normal.x; v9 = v2->vPosition.y - stru_721530.normal.y; v10 = stru_721530.prolly_normal_d + v4; v11 = (v8 * stru_721530.direction.y - v9 * stru_721530.direction.x) >> 16; v12 = (v8 * stru_721530.direction.x + v9 * stru_721530.direction.y) >> 16; if ( abs(v11) > v10 || v12 <= 0) return false; if (fixpoint_mul(stru_721530.direction.z, v12) + stru_721530.normal.z < v2->vPosition.z) return false; v13 = v12 - integer_sqrt(v10 * v10 - v11 * v11); if ( v13 < 0 ) v13 = 0; if ( v13 < stru_721530.field_7C ) { stru_721530.field_7C = v13; stru_721530.uFaceID = PID(OBJECT_Actor,a1); } return true; } //----- (00401A91) -------------------------------------------------------- void Actor::UpdateActorAI() { signed int v4; // edi@10 signed int sDmg; // eax@14 Player *pPlayer; // ecx@21 Actor *pActor; // esi@34 //unsigned __int16 v22; // ax@86 unsigned int v27; // ecx@123 unsigned int v28; // eax@123 int v33; // eax@144 int v34; // eax@147 char v35; // al@150 unsigned int v36; // edi@152 signed int v37; // eax@154 double v42; // st7@176 double v43; // st6@176 int v45; // eax@192 unsigned __int8 v46; // cl@197 signed int v47; // st7@206 uint v58; // st7@246 unsigned int v65; // [sp-10h] [bp-C0h]@144 int v70; // [sp-10h] [bp-C0h]@213 AIDirection v72; // [sp+0h] [bp-B0h]@246 AIDirection a3; // [sp+1Ch] [bp-94h]@129 int target_pid_type; // [sp+70h] [bp-40h]@83 signed int a1; // [sp+74h] [bp-3Ch]@129 int v78; // [sp+78h] [bp-38h]@79 AIDirection* pDir; // [sp+7Ch] [bp-34h]@129 float radiusMultiplier; // [sp+98h] [bp-18h]@33 int v81; // [sp+9Ch] [bp-14h]@100 signed int target_pid; // [sp+ACh] [bp-4h]@83 AIState uAIState; uint v38; //Build AI array if ( uCurrentlyLoadedLevelType == LEVEL_Outdoor) Actor::MakeActorAIList_ODM(); else Actor::MakeActorAIList_BLV(); //Armageddon damage mechanic if ( uCurrentlyLoadedLevelType != LEVEL_Indoor && pParty->armageddon_timer > 0 ) { if ( pParty->armageddon_timer > 417 ) pParty->armageddon_timer = 0; else { pParty->sRotationY = (stru_5C6E00->uIntegerDoublePi - 1) & (pParty->sRotationY + rand() % 16 - 8); pParty->sRotationX = pParty->sRotationX + rand() % 16 - 8; if ( pParty->sRotationX > 128) pParty->sRotationX = 128; else if ( pParty->sRotationX < -128 ) pParty->sRotationX = -128; pParty->uFlags |= 2u; pParty->armageddon_timer -= pMiscTimer->uTimeElapsed; v4 = pParty->armageddonDamage + 50; if ( pParty->armageddon_timer <= 0 ) { pParty->armageddon_timer = 0; for(size_t i = 0; i < uNumActors; i++) { pActor=&pActors[i]; if ( pActor->CanAct() ) { sDmg = pActor->CalcMagicalDamageToActor((DAMAGE_TYPE)5, v4); pActor->sCurrentHP -= sDmg; if ( sDmg ) { if ( pActor->sCurrentHP >= 0 ) Actor::AI_Stun(i, 4, 0); else { Actor::Die(i); if ( pActor->pMonsterInfo.uExp ) pParty->GivePartyExp(pMonsterStats->pInfos[pActor->pMonsterInfo.uID].uExp); } } } } for(int i = 1; i <= 4; i++) { pPlayer = pPlayers[i]; if ( !pPlayer->pConditions[Condition_Dead] && !pPlayer->pConditions[Condition_Pertified] && !pPlayer->pConditions[Condition_Eradicated] ) pPlayer->ReceiveDamage(v4, DMGT_MAGICAL); } } if (pTurnEngine->pending_actions) --pTurnEngine->pending_actions; } } //Turn-based mode: return if (pParty->bTurnBasedModeOn) { pTurnEngine->AITurnBasedAction(); return; } for (uint i = 0; i < uNumActors; ++i) { pActor = &pActors[i]; ai_near_actors_targets_pid[i] = OBJECT_Player; //Skip actor if: Dead / Removed / Disabled / uAttributes & 0x0400 if (pActor->uAIState == Dead || pActor->uAIState == Removed || pActor->uAIState == Disabled || pActor->uAttributes & ACTOR_ALIVE) continue; //Kill actor if HP == 0 if (!pActor->sCurrentHP && pActor->uAIState != Dying) Actor::Die(i); //Kill buffs if expired for (uint j = 0; j < 22; ++j) { if (j != 10) pActor->pActorBuffs[j].IsBuffExpiredToTime(pParty->uTimePlayed); } //If shrink expired: reset height if (pActor->pActorBuffs[ACTOR_BUFF_SHRINK].uExpireTime < 0) pActor->uActorHeight = pMonsterList->pMonsters[pActor->pMonsterInfo.uID - 1].uMonsterHeight; //If Charm still active: make actor friendly if (pActor->pActorBuffs[ACTOR_BUFF_CHARM].uExpireTime > 0) pActor->pMonsterInfo.uHostilityType = MonsterInfo::Hostility_Friendly; //Else: reset hostilty else if (pActor->pActorBuffs[ACTOR_BUFF_CHARM].uExpireTime < 0) pActor->pMonsterInfo.uHostilityType = pMonsterStats->pInfos[pActor->pMonsterInfo.uID].uHostilityType; //If actor Paralyzed or Stoned: skip if (pActor->pActorBuffs[ACTOR_BUFF_PARALYZED].uExpireTime > 0 || pActor->pActorBuffs[ACTOR_BUFF_STONED].uExpireTime > 0) continue; //Calculate RecoveryTime pActor->pMonsterInfo.uRecoveryTime = max(pActor->pMonsterInfo.uRecoveryTime - pMiscTimer->uTimeElapsed, 0); pActor->uCurrentActionTime += pMiscTimer->uTimeElapsed; if (pActor->uCurrentActionTime < pActor->uCurrentActionLength) continue; if (pActor->uAIState == Dying) pActor->uAIState = Dead; else { if (pActor->uAIState != Summoned) { Actor::AI_StandOrBored(i, OBJECT_Player, 256, nullptr); continue; } pActor->uAIState = Standing; } pActor->uCurrentActionTime = 0; pActor->uCurrentActionLength = 0; pActor->UpdateAnimation(); } for(v78 = 0; v78 < ai_arrays_size; ++v78) { uint actor_id = ai_near_actors_ids[v78]; assert(actor_id < uNumActors); pActor = &pActors[actor_id]; v47 = (signed int)(pActor->pMonsterInfo.uRecoveryTime * 2.133333333333333); Actor::_SelectTarget(actor_id, &ai_near_actors_targets_pid[actor_id], true); if (pActor->pMonsterInfo.uHostilityType && !ai_near_actors_targets_pid[actor_id]) pActor->pMonsterInfo.uHostilityType = MonsterInfo::Hostility_Friendly; target_pid = ai_near_actors_targets_pid[actor_id]; target_pid_type = PID_TYPE(target_pid); if ( target_pid_type == OBJECT_Actor) radiusMultiplier = 0.5; else radiusMultiplier = 1.0; //v22 = pActor->uAIState; if ( pActor->uAIState == Dying || pActor->uAIState == Dead || pActor->uAIState == Removed || pActor->uAIState == Disabled || pActor->uAIState == Summoned) continue; if ( !pActor->sCurrentHP ) Actor::Die(actor_id); for( int i = 0;i < 22; i++ ) { if ( i != 10 ) pActor->pActorBuffs[i].IsBuffExpiredToTime(pParty->uTimePlayed); } if ( pActor->pActorBuffs[ACTOR_BUFF_SHRINK].uExpireTime < 0 ) pActor->uActorHeight = pMonsterList->pMonsters[pActor->pMonsterInfo.uID - 1].uMonsterHeight; if ( pActor->pActorBuffs[ACTOR_BUFF_CHARM].uExpireTime > 0 ) pActor->pMonsterInfo.uHostilityType = MonsterInfo::Hostility_Friendly; else if ( pActor->pActorBuffs[ACTOR_BUFF_CHARM].uExpireTime < 0 ) pActor->pMonsterInfo.uHostilityType = pMonsterStats->pInfos[pActor->pMonsterInfo.uID].uHostilityType; //If actor is summoned and buff expired: continue and set state to Removed if ( pActor->pActorBuffs[ACTOR_BUFF_SUMMONED].uExpireTime < 0 ) { pActor->uAIState = Removed; continue; } if ( (signed __int64)pActor->pActorBuffs[ACTOR_BUFF_STONED].uExpireTime > 0 || (signed __int64)pActor->pActorBuffs[ACTOR_BUFF_PARALYZED].uExpireTime > 0) { continue; } v27 = pMiscTimer->uTimeElapsed; v28 = pActor->pMonsterInfo.uRecoveryTime; pActor->uCurrentActionTime += pMiscTimer->uTimeElapsed; if ( (signed int)v28 > 0 ) pActor->pMonsterInfo.uRecoveryTime = v28 - v27; if ( pActor->pMonsterInfo.uRecoveryTime < 0 ) pActor->pMonsterInfo.uRecoveryTime = 0; if ( !pActor->ActorNearby() ) pActor->uAttributes |= ACTOR_NEARBY; a1 = PID(OBJECT_Actor,actor_id); Actor::GetDirectionInfo(PID(OBJECT_Actor,actor_id), target_pid, &a3, 0); pDir = &a3; uAIState = pActor->uAIState; if ( pActor->pMonsterInfo.uHostilityType == MonsterInfo::Hostility_Friendly || (signed int)pActor->pMonsterInfo.uRecoveryTime > 0 || radiusMultiplier * 307.2 < pDir->uDistance || uAIState != Pursuing && uAIState != Standing && uAIState != Tethered && uAIState != Fidgeting && !pActor->pMonsterInfo.uMissleAttack1Type || uAIState != Stunned ) { if ( (signed int)pActor->uCurrentActionTime < pActor->uCurrentActionLength ) continue; else if ( pActor->uAIState == AttackingMelee ) { v35 = pActor->special_ability_use_check(actor_id); AttackerInfo.Add(a1, 5120, pActor->vPosition.x, pActor->vPosition.y, pActor->vPosition.z + ((signed int)pActor->uActorHeight >> 1), v35, 1 ); } else if ( pActor->uAIState == AttackingRanged1 ) { v34 = pActor->pMonsterInfo.uMissleAttack1Type; Actor::AI_RangedAttack(actor_id, pDir, v34, 0); } else if ( pActor->uAIState == AttackingRanged2 ) { v34 = pActor->pMonsterInfo.uMissleAttack2Type; Actor::AI_RangedAttack(actor_id, pDir, v34, 1); } else if ( pActor->uAIState == AttackingRanged3 ) { v65 = pActor->pMonsterInfo.uSpellSkillAndMastery1; v33 = pActor->pMonsterInfo.uSpell1ID; Actor::AI_SpellAttack(actor_id, pDir, v33, 2, v65); } else if ( pActor->uAIState == AttackingRanged4 ) { v65 = pActor->pMonsterInfo.uSpellSkillAndMastery2; v33 = pActor->pMonsterInfo.uSpell2ID; Actor::AI_SpellAttack(actor_id, pDir, v33, 3, v65); } } v36 = pDir->uDistance; if ( pActor->pMonsterInfo.uHostilityType == MonsterInfo::Hostility_Friendly) { if ( target_pid_type == OBJECT_Actor ) { v36 = pDir->uDistance; v37 =pFactionTable->relations[(pActor->pMonsterInfo.uID-1) / 3 + 1][(pActors[PID_ID(target_pid)].pMonsterInfo.uID - 1) / 3 + 1]; } else v37 = 4; v38 = 0; if ( v37 == 2 ) v38 = 1024; else if ( v37 == 3 ) v38 = 2560; else if ( v37 == 4 ) v38 = 5120; if ( v37 >= 1 && v37 <= 4 && v36 < v38 || v37 == 1 ) pActor->pMonsterInfo.uHostilityType = MonsterInfo::Hostility_Long; } //If actor afraid: flee or if out of range random move if (pActor->pActorBuffs[ACTOR_BUFF_AFRAID].uExpireTime > 0) { if ( (signed int)v36 >= 10240 ) Actor::AI_RandomMove(actor_id, target_pid, 1024, 0); else Actor::AI_Flee(actor_id, target_pid, 0, pDir); continue; } if ( pActor->pMonsterInfo.uHostilityType == MonsterInfo::Hostility_Long && target_pid ) { if ( pActor->pMonsterInfo.uAIType == 1 ) { if ( pActor->pMonsterInfo.uMovementType == MONSTER_MOVEMENT_TYPE_STAIONARY ) Actor::AI_Stand(actor_id, target_pid, (uint)(pActor->pMonsterInfo.uRecoveryTime * 2.133333333333333), pDir); else { Actor::AI_Flee(actor_id, target_pid, 0, pDir); continue; } } if ( !(pActor->uAttributes & ACTOR_FLEEING) ) { if ( pActor->pMonsterInfo.uAIType == 2 || pActor->pMonsterInfo.uAIType == 3) { if ( pActor->pMonsterInfo.uAIType == 2 ) v43 = (double)(signed int)pActor->pMonsterInfo.uHP * 0.2; if ( pActor->pMonsterInfo.uAIType == 3 ) v43 = (double)(signed int)pActor->pMonsterInfo.uHP * 0.1; v42 = (double)pActor->sCurrentHP; if ( v43 > v42 && (signed int)v36 < 10240 ) { Actor::AI_Flee(actor_id, target_pid, 0, pDir); continue; } } } v81 = v36 - pActor->uActorRadius; if ( target_pid_type == OBJECT_Actor ) v81 -= pActors[PID_ID(target_pid)].uActorRadius; if ( v81 < 0 ) v81 = 0; rand(); pActor->uAttributes &= ~ACTOR_UNKNOW5;//~0x40000 if ( v81 < 5120 ) { v45 = pActor->special_ability_use_check(actor_id); if ( v45 == 0 ) { if ( pActor->pMonsterInfo.uMissleAttack1Type ) { if ( (signed int)pActor->pMonsterInfo.uRecoveryTime <= 0 ) Actor::AI_MissileAttack1(actor_id, target_pid, pDir); else if ( pActor->pMonsterInfo.uMovementType == MONSTER_MOVEMENT_TYPE_STAIONARY ) Actor::AI_Stand(actor_id, target_pid, v47, pDir); else { if ( radiusMultiplier * 307.2 > (double)v81 ) Actor::AI_Stand(actor_id, target_pid, v47, pDir); else Actor::AI_Pursue1(actor_id, target_pid, actor_id, v47, pDir); } } else { if ( (double)v81 >= radiusMultiplier * 307.2 ) { if (pActor->pMonsterInfo.uMovementType == MONSTER_MOVEMENT_TYPE_STAIONARY) Actor::AI_Stand(actor_id, target_pid, v47, pDir); else if ( v81 >= 1024 )//monsters Actor::AI_Pursue3(actor_id, target_pid, 0, pDir); else { v70 = (signed int)(radiusMultiplier * 307.2); //monsters //guard after player runs away // follow player Actor::AI_Pursue2(actor_id, target_pid, 0, pDir, v70); } } else if ( (signed int)pActor->pMonsterInfo.uRecoveryTime > 0 ) { Actor::AI_Stand(actor_id, target_pid, v47, pDir); } else { //monsters Actor::AI_MeleeAttack(actor_id, target_pid, pDir); } } continue; } else if ( v45 == 2 || v45 == 3 ) { if ( v45 == 2 ) v46 = pActor->pMonsterInfo.uSpell1ID; else v46 = pActor->pMonsterInfo.uSpell2ID; if ( v46 ) { if ( (signed int)pActor->pMonsterInfo.uRecoveryTime <= 0 ) { if ( v45 == 2 ) Actor::AI_SpellAttack1(actor_id, target_pid, pDir); else Actor::AI_SpellAttack2(actor_id, target_pid, pDir); } else if ( radiusMultiplier * 307.2 > (double)v81 || pActor->pMonsterInfo.uMovementType == MONSTER_MOVEMENT_TYPE_STAIONARY ) Actor::AI_Stand(actor_id, target_pid, v47, pDir); else Actor::AI_Pursue1(actor_id, target_pid, actor_id, v47, pDir); } else { if ( (double)v81 >= radiusMultiplier * 307.2 ) { if ( pActor->pMonsterInfo.uMovementType == MONSTER_MOVEMENT_TYPE_STAIONARY ) Actor::AI_Stand(actor_id, target_pid, v47, pDir); else if ( v81 >= 1024 ) Actor::AI_Pursue3(actor_id, target_pid, 256, pDir); else { v70 = (signed int)(radiusMultiplier * 307.2); Actor::AI_Pursue2(actor_id, target_pid, 0, pDir, v70); } } else if ( (signed int)pActor->pMonsterInfo.uRecoveryTime > 0 ) { Actor::AI_Stand(actor_id, target_pid, v47, pDir); } else { Actor::AI_MeleeAttack(actor_id, target_pid, pDir); } } continue; } } } if ( pActor->pMonsterInfo.uHostilityType != MonsterInfo::Hostility_Long || !target_pid || v81 >= 5120 || v45 != 1 ) { if ( pActor->pMonsterInfo.uMovementType == MONSTER_MOVEMENT_TYPE_SHORT ) Actor::AI_RandomMove(actor_id, 4, 1024, 0); else if ( pActor->pMonsterInfo.uMovementType == MONSTER_MOVEMENT_TYPE_MEDIUM ) Actor::AI_RandomMove(actor_id, 4, 2560, 0); else if ( pActor->pMonsterInfo.uMovementType == MONSTER_MOVEMENT_TYPE_LONG ) Actor::AI_RandomMove(actor_id, 4, 5120, 0); else if ( pActor->pMonsterInfo.uMovementType == MONSTER_MOVEMENT_TYPE_FREE ) Actor::AI_RandomMove(actor_id, 4, 10240, 0); else if ( pActor->pMonsterInfo.uMovementType == MONSTER_MOVEMENT_TYPE_STAIONARY ) { Actor::GetDirectionInfo(a1, 4, &v72, 0); v58 = (uint)(pActor->pMonsterInfo.uRecoveryTime * 2.133333333333333); Actor::AI_Stand(actor_id, 4, v58, &v72); } } else if ( !pActor->pMonsterInfo.uMissleAttack2Type ) { if ( (double)v81 >= radiusMultiplier * 307.2 ) { if ( pActor->pMonsterInfo.uMovementType == MONSTER_MOVEMENT_TYPE_STAIONARY ) Actor::AI_Stand(actor_id, target_pid, v47, pDir); else if ( v81 >= 1024 ) Actor::AI_Pursue3(actor_id, target_pid, 256, pDir); else { v70 = (int)(radiusMultiplier * 307.2); Actor::AI_Pursue2(actor_id, target_pid, 0, pDir, v70); } } else if ( (signed int)pActor->pMonsterInfo.uRecoveryTime > 0 ) Actor::AI_Stand(actor_id, target_pid, v47, pDir); else Actor::AI_MeleeAttack(actor_id, target_pid, pDir); } else if ( (signed int)pActor->pMonsterInfo.uRecoveryTime > 0 ) { if ( radiusMultiplier * 307.2 > (double)v81 || pActor->pMonsterInfo.uMovementType == MONSTER_MOVEMENT_TYPE_STAIONARY ) Actor::AI_Stand(actor_id, target_pid, v47, pDir); else Actor::AI_Pursue1(actor_id, target_pid, actor_id, v47, pDir); } else Actor::AI_MissileAttack2(actor_id, target_pid, pDir); } } //----- (0044665D) -------------------------------------------------------- // uType: 0 -> any monster // 1 -> uParam is GroupID // 2 -> uParam is MonsterID // 3 -> uParam is ActorID // uNumAlive: 0 -> all must be alive int __fastcall IsActorAlive(unsigned int uType, unsigned int uParam, unsigned int uNumAlive) { unsigned int uAliveActors; // eax@6 unsigned int uTotalActors; // [sp+0h] [bp-4h]@1 uTotalActors = 0; if ( uType ) { if ( uType == 1 ) uAliveActors = Actor::SearchActorByGroup(&uTotalActors, uParam); else { if ( uType == 2 ) uAliveActors = Actor::SearchActorByMonsterID(&uTotalActors, uParam); else { if ( uType != 3 ) return 0; uAliveActors = Actor::SearchActorByID(&uTotalActors, uParam); } } } else uAliveActors = Actor::SearchAliveActors(&uTotalActors); if (uNumAlive) return uAliveActors >= uNumAlive; else return uTotalActors == uAliveActors; } //----- (00408B54) -------------------------------------------------------- unsigned int Actor::SearchActorByID(unsigned int *pTotalActors, unsigned int a2) { //int v4; // eax@1 unsigned int result; // ebx@1 //v4 = GetAlertStatus(); *pTotalActors = 0; result = 0; if ( (pActors[a2].uAttributes & ACTOR_UNKNOW7) == GetAlertStatus() ) { *pTotalActors = 1; if ( pActors[a2].IsNotAlive() == 1 ) result = 1; } return result; } //----- (00408AE7) -------------------------------------------------------- unsigned int Actor::SearchActorByGroup(unsigned int *pTotalActors, unsigned int uGroup) { unsigned int result; // [sp+10h] [bp-4h]@1 int v8 = GetAlertStatus(); *pTotalActors = 0; result = 0; for ( uint i = 0; i < uNumActors; i++) { if ( (pActors[i].uAttributes & ACTOR_UNKNOW7) == v8 && pActors[i].uGroup == uGroup) { ++*pTotalActors; if ( pActors[i].IsNotAlive() == 1 ) ++result; } } return result; } //----- (00408A7E) -------------------------------------------------------- unsigned int Actor::SearchActorByMonsterID(unsigned int *pTotalActors, int uMonsterID) { int v8; // [sp+Ch] [bp-8h]@1 unsigned int result; // [sp+10h] [bp-4h]@1 v8 = GetAlertStatus(); *pTotalActors = 0; result = 0; for ( uint i = 0; i < uNumActors; i++) { if ( (pActors[i].uAttributes & ACTOR_UNKNOW7) == v8 && pActors[i].pMonsterInfo.field_33 == uMonsterID) { ++*pTotalActors; if ( pActors[i].IsNotAlive() == 1 ) ++result; } } return result; } //----- (00408A27) -------------------------------------------------------- unsigned int Actor::SearchAliveActors(unsigned int *pTotalActors) { int v2; // eax@1 unsigned int result; // ebp@1 v2 = GetAlertStatus(); result = 0; *pTotalActors = 0; for ( uint i = 0; i < uNumActors; i++) { if ( (pActors[i].uAttributes & ACTOR_UNKNOW7) == v2 ) { ++*pTotalActors; if ( pActors[i].IsNotAlive() == 1 ) ++result; } } return result; } //----- (00408768) -------------------------------------------------------- void Actor::InitializeActors() { bool evil; // [sp+Ch] [bp-10h]@1 bool bPit; // [sp+10h] [bp-Ch]@1 bool good; // [sp+14h] [bp-8h]@1 bool bCelestia; // [sp+18h] [bp-4h]@1 bCelestia = false; bPit = false; good = false; evil = false; if ( !_stricmp(pCurrentMapName, "d25.blv") )//the Celestia bCelestia = true; if ( !_stricmp(pCurrentMapName, "d26.blv") )//the Pit bPit = true; if (pParty->IsPartyGood()) good = true; if (pParty->IsPartyEvil()) evil = true; Log::Warning(L"%S %S %u", __FILE__, __FUNCTION__, __LINE__); // ai_near_actors_targets_pid[i] for AI_Stand seems always 0; original code behaviour is identical for (uint i = 0; i < uNumActors; ++i) { Actor* actor = &pActors[i]; if (actor->CanAct() || actor->uAIState == Disabled) { actor->vPosition.x = actor->vInitialPosition.x; actor->vPosition.y = actor->vInitialPosition.y; actor->vPosition.z = actor->vInitialPosition.z; actor->sCurrentHP = actor->pMonsterInfo.uHP; if (actor->uAIState != Disabled) { Actor::AI_Stand(i, ai_near_actors_targets_pid[i], actor->pMonsterInfo.uRecoveryTime, 0); } } actor->pMonsterInfo.uHostilityType = MonsterInfo::Hostility_Friendly; if (!bCelestia || good) if (!bPit || evil) if (actor->IsPeasant()) actor->ResetAggressor();//~0x80000 actor->ResetHasItem();//~0x800000 if (actor->uAttributes & ACTOR_UNKNOW9) Actor::_4031C1_update_job_never_gets_called(i, pParty->uCurrentHour, 1); } } //----- (00439474) -------------------------------------------------------- void Actor::DamageMonsterFromParty(signed int a1, unsigned int uActorID_Monster, Vec3_int_ *pVelocity) { SpriteObject *projectileSprite; // ebx@1 Actor *pMonster; // esi@7 unsigned __int16 v16; // cx@25 int v33; // eax@100 int v40; // ebx@107 int extraRecoveryTime; // qax@125 unsigned __int16 v43; // ax@132 unsigned __int16 v45; // ax@132 unsigned __int64 v46; // [sp+Ch] [bp-60h]@6 char *pPlayerName; // [sp+18h] [bp-54h]@12 char *pMonsterName; // [sp+1Ch] [bp-50h]@6 signed int a4; // [sp+44h] [bp-28h]@1 bool IsAdditionalDamagePossible; // [sp+50h] [bp-1Ch]@1 int v61; // [sp+58h] [bp-14h]@1 bool isLifeStealing; // [sp+5Ch] [bp-10h]@1 int uDamageAmount; // [sp+60h] [bp-Ch]@1 DAMAGE_TYPE attackElement; // [sp+64h] [bp-8h]@27 projectileSprite = 0; uDamageAmount = 0; a4 = 0; v61 = 0; IsAdditionalDamagePossible = false; isLifeStealing = 0; if ( PID_TYPE(a1) == OBJECT_Item) { projectileSprite = &pSpriteObjects[PID_ID(a1)]; v61 = projectileSprite->field_60_distance_related_prolly_lod; a1 = projectileSprite->spell_caster_pid; } if (PID_TYPE(a1) != OBJECT_Player) return; assert(PID_ID(abs(a1)) < 4); Player* player = &pParty->pPlayers[PID_ID(a1)]; pMonster = &pActors[uActorID_Monster]; if (pMonster->IsNotAlive()) return; pMonster->uAttributes |= 0xC000; if ( pMonster->uAIState == Fleeing ) pMonster->uAttributes |= ACTOR_FLEEING; bool hit_will_stun = false, hit_will_paralyze = false; if ( !projectileSprite ) { int main_hand_idx = player->pEquipment.uMainHand; IsAdditionalDamagePossible = true; if ( player->HasItemEquipped(EQUIP_TWO_HANDED) ) { uint main_hand_skill = player->GetMainHandItem()->GetPlayerSkillType(); uint main_hand_mastery = SkillToMastery(player->pActiveSkills[main_hand_skill]); switch (main_hand_skill) { case PLAYER_SKILL_STAFF: if (main_hand_mastery >= 3) { if (rand() % 100 < (player->GetActualSkillLevel(PLAYER_SKILL_STAFF) & 0x3F)) // stun chance when mastery >= 3 hit_will_stun = true; } break; case PLAYER_SKILL_MACE: if (main_hand_mastery >= 3) { if (rand() % 100 < (player->GetActualSkillLevel(PLAYER_SKILL_MACE) & 0x3F)) hit_will_stun = true; } if (main_hand_mastery >= 4) { if (rand() % 100 < (player->GetActualSkillLevel(PLAYER_SKILL_MACE) & 0x3F)) hit_will_paralyze = true; } break; } } attackElement = DMGT_PHISYCAL; uDamageAmount = player->CalculateMeleeDamageTo(false, false, pMonster->pMonsterInfo.uID); if ( !player->PlayerHitOrMiss(pMonster, v61, a4) ) { player->PlaySound(SPEECH_52, 0); return; } } else { v61 = projectileSprite->field_60_distance_related_prolly_lod; if ( projectileSprite->spell_id != SPELL_DARK_SOULDRINKER ) { int d1 = abs(pParty->vPosition.x - projectileSprite->vPosition.x); int d2 = abs(pParty->vPosition.y - projectileSprite->vPosition.y); int d3 = abs(pParty->vPosition.z - projectileSprite->vPosition.z); v61 = int_get_vector_length(d1, d2, d3); if ( v61 >= 5120 && !(pMonster->uAttributes & ACTOR_ALIVE) )//0x400 return; else if ( v61 >= 2560 ) v61 = 2; else v61 = 1; } switch (projectileSprite->spell_id) { case SPELL_LASER_PROJECTILE: v16 = player->pActiveSkills[PLAYER_SKILL_BLASTER]; v61 = 1; if ( SkillToMastery(v16) >= 3 ) a4 = player->pActiveSkills[PLAYER_SKILL_BLASTER] & 0x3F; attackElement = DMGT_PHISYCAL; uDamageAmount = player->CalculateMeleeDamageTo(true, true, 0); if ( !player->PlayerHitOrMiss(pMonster, v61, a4) ) { player->PlaySound(SPEECH_52, 0); return; } break; case SPELL_101: attackElement = DMGT_FIRE; uDamageAmount = player->CalculateRangedDamageTo(0); if ( pMonster->pActorBuffs[ACTOR_BUFF_SHIELD].uExpireTime > 0 ) uDamageAmount >>= 1; IsAdditionalDamagePossible = true; if ( !player->PlayerHitOrMiss(pMonster, v61, a4) ) { player->PlaySound(SPEECH_52, 0); return; } break; case SPELL_EARTH_BLADES: a4 = 5 * projectileSprite->spell_level; attackElement = (DAMAGE_TYPE)player->GetSpellSchool(SPELL_EARTH_BLADES); uDamageAmount = _43AFE3_calc_spell_damage(39, projectileSprite->spell_level, projectileSprite->spell_skill, pMonster->sCurrentHP); if ( pMonster->pActorBuffs[ACTOR_BUFF_SHIELD].uExpireTime > 0 ) uDamageAmount >>= 1; IsAdditionalDamagePossible = false; if ( !player->PlayerHitOrMiss( pMonster, v61, a4) ) { player->PlaySound(SPEECH_52, 0); return; } break; case SPELL_EARTH_STUN: uDamageAmount = 0; attackElement = DMGT_PHISYCAL; hit_will_stun = 1; if ( !player->PlayerHitOrMiss( pMonster, v61, a4) ) { player->PlaySound(SPEECH_52, 0); return; } break; case SPELL_BOW_ARROW: attackElement = DMGT_PHISYCAL; uDamageAmount = player->CalculateRangedDamageTo(pMonster->word_000086_some_monster_id); if ( pMonster->pActorBuffs[ACTOR_BUFF_SHIELD].uExpireTime > 0 ) uDamageAmount /= 2; IsAdditionalDamagePossible = true; if ( projectileSprite->stru_24.uItemID != 0 && projectileSprite->stru_24.uSpecEnchantmentType == 3 ) //of carnage { attackElement = DMGT_FIRE; } else if ( !player->PlayerHitOrMiss( pMonster, v61, a4) ) { player->PlaySound(SPEECH_52, 0); return; } break; default: attackElement = (DAMAGE_TYPE)player->GetSpellSchool(projectileSprite->spell_id); IsAdditionalDamagePossible = false; uDamageAmount = _43AFE3_calc_spell_damage(projectileSprite->spell_id, projectileSprite->spell_level, projectileSprite->spell_skill, pMonster->sCurrentHP); break; } } if (player->IsWeak()) uDamageAmount /= 2; if ( pMonster->pActorBuffs[ACTOR_BUFF_STONED].uExpireTime > 0 ) uDamageAmount = 0; v61 = pMonster->CalcMagicalDamageToActor(attackElement, uDamageAmount); if ( !projectileSprite && player->IsUnarmed() && player->pPlayerBuffs[PLAYER_BUFF_HAMMERHANDS].uExpireTime > 0 ) { v61 += pMonster->CalcMagicalDamageToActor((DAMAGE_TYPE)8, player->pPlayerBuffs[PLAYER_BUFF_HAMMERHANDS].uPower); } uDamageAmount = v61; if ( IsAdditionalDamagePossible ) { if ( projectileSprite ) { a4 = projectileSprite->stru_24._439DF3_get_additional_damage((int*)&attackElement, &isLifeStealing); if ( isLifeStealing && pMonster->sCurrentHP > 0 ) { player->sHealth += v61 / 5; if ( player->sHealth > player->GetMaxHealth() ) player->sHealth = player->GetMaxHealth(); } uDamageAmount += pMonster->CalcMagicalDamageToActor(attackElement, a4); } else { for (int i = 0; i < 2; i++) { if ( player->HasItemEquipped((ITEM_EQUIP_TYPE)i) ) { ItemGen* item; if (i == 0) item = player->GetOffHandItem(); else item = player->GetMainHandItem(); a4 = item->_439DF3_get_additional_damage((int*)&attackElement, &isLifeStealing); if ( isLifeStealing && pMonster->sCurrentHP > 0 ) { player->sHealth += v61 / 5; if ( player->sHealth > player->GetMaxHealth() ) player->sHealth = player->GetMaxHealth(); } uDamageAmount += pMonster->CalcMagicalDamageToActor(attackElement, a4); } } } } pMonster->sCurrentHP -= uDamageAmount; if ( uDamageAmount == 0 && !hit_will_stun ) { player->PlaySound(SPEECH_52, 0); return; } if ( pMonster->sCurrentHP > 0 ) { Actor::AI_Stun(uActorID_Monster, a1, 0); Actor::AggroSurroundingPeasants(uActorID_Monster, 1); if ( bShowDamage ) { if ( projectileSprite ) sprintfex(pTmpBuf.data(), pGlobalTXT_LocalizationStrings[189], player->pName, pMonster->pActorName, uDamageAmount);// "%s shoots %s for %lu points" else sprintfex(pTmpBuf.data(), pGlobalTXT_LocalizationStrings[164], player->pName, pMonster->pActorName, uDamageAmount);// "%s hits %s for %lu damage" ShowStatusBarString(pTmpBuf.data(), 2u); } } else { if ( pMonsterStats->pInfos[pMonster->pMonsterInfo.uID].bQuestMonster & 1 ) { if ( /*pRenderer->pRenderD3D &&*/ pEngine->uFlags2 & GAME_FLAGS_2_DRAW_BLOODSPLATS ) { v33 = byte_4D864C && pEngine->uFlags & 0x80000 ? 10 * pMonster->uActorRadius : pMonster->uActorRadius; pDecalBuilder->AddBloodsplat((float)pMonster->vPosition.x, (float)pMonster->vPosition.y, (float)pMonster->vPosition.z, 1.0, 0.0, 0.0, (float)v33, 0, 0); } } Actor::Die(uActorID_Monster); Actor::ApplyFineForKillingPeasant(uActorID_Monster); Actor::AggroSurroundingPeasants(uActorID_Monster, 1); if ( pMonster->pMonsterInfo.uExp ) pParty->GivePartyExp(pMonsterStats->pInfos[pMonster->pMonsterInfo.uID].uExp); v40 = SPEECH_51; if ( rand() % 100 < 20 ) v40 = ((signed int)pMonster->pMonsterInfo.uHP >= 100) + 1; player->PlaySound((PlayerSpeech)v40, 0); if ( bShowDamage ) { pMonsterName = (char *)uDamageAmount; pPlayerName = player->pName; // "%s inflicts %lu points killing %s" sprintfex(pTmpBuf.data(), pGlobalTXT_LocalizationStrings[175], player->pName, uDamageAmount, pMonster); ShowStatusBarString(pTmpBuf.data(), 2u); } } if ( pMonster->pActorBuffs[ACTOR_BUFF_PAIN_REFLECTION].uExpireTime > 0 && uDamageAmount != 0 ) player->ReceiveDamage(uDamageAmount, attackElement); int knockbackValue = 20 * v61 / (signed int)pMonster->pMonsterInfo.uHP; if ( (player->GetSpecialItemBonus(24) || hit_will_stun) && pMonster->DoesDmgTypeDoDamage(DMGT_EARTH) ) { extraRecoveryTime = 20; knockbackValue = 10; if ( !pParty->bTurnBasedModeOn ) extraRecoveryTime = (int)(flt_6BE3A8_debug_recmod2 * 42.66666666666666); pMonster->pMonsterInfo.uRecoveryTime += extraRecoveryTime; if ( bShowDamage ) { pMonsterName = player->pName; // "%s stuns %s" sprintfex(pTmpBuf.data(), pGlobalTXT_LocalizationStrings[635], player->pName, pMonster); ShowStatusBarString(pTmpBuf.data(), 2u); } } if ( hit_will_paralyze && pMonster->CanAct() && pMonster->DoesDmgTypeDoDamage(DMGT_EARTH)) { v43 = player->GetActualSkillLevel(PLAYER_SKILL_MACE); v45 = SkillToMastery(v43); v46 = pParty->uTimePlayed + (signed int)(signed __int64)((double)(signed int)(7680 * (v43 & 0x3F)) * 0.033333335); pMonster->pActorBuffs[ACTOR_BUFF_PARALYZED].Apply(v46, v45, 0, 0, 0); if ( bShowDamage ) { pMonsterName = player->pName; // "%s paralyzes %s" sprintfex(pTmpBuf.data(), pGlobalTXT_LocalizationStrings[636], player->pName, pMonster); ShowStatusBarString(pTmpBuf.data(), 2u); } } if ( knockbackValue > 10 ) knockbackValue = 10; if ( !MonsterStats::BelongsToSupertype(pMonster->pMonsterInfo.uID, MONSTER_SUPERTYPE_TREANT) ) { pVelocity->x = fixpoint_mul(knockbackValue, pVelocity->x); pVelocity->y = fixpoint_mul(knockbackValue, pVelocity->y); pVelocity->z = fixpoint_mul(knockbackValue, pVelocity->z); pMonster->vVelocity.x = 50 * LOWORD(pVelocity->x); pMonster->vVelocity.y = 50 * LOWORD(pVelocity->y); pMonster->vVelocity.z = 50 * LOWORD(pVelocity->z); } Actor::AddBloodsplatOnDamageOverlay(uActorID_Monster, 1, v61); } //----- (004BBF61) -------------------------------------------------------- void Actor::Arena_summon_actor( int monster_id, __int16 x, int y, int z ) { int v12; // ebx@7 int v13; // eax@8 __int16 v16; // [sp+10h] [bp-4h]@3 if (uNumActors < 500) { v16 = 0; if ( uCurrentlyLoadedLevelType == LEVEL_Indoor ) v16 = pIndoor->GetSector(x, y, z); pActors[uNumActors].Reset(); strcpy(pActors[uNumActors].pActorName, pMonsterStats->pInfos[monster_id].pName); pActors[uNumActors].sCurrentHP = LOWORD(pMonsterStats->pInfos[monster_id].uHP); memcpy(&pActors[uNumActors].pMonsterInfo, &pMonsterStats->pInfos[monster_id], 0x58u); pActors[uNumActors].word_000086_some_monster_id = monster_id; pActors[uNumActors].uActorRadius = pMonsterList->pMonsters[monster_id - 1].uMonsterRadius; pActors[uNumActors].uActorHeight = pMonsterList->pMonsters[monster_id - 1].uMonsterHeight; pActors[uNumActors].uMovementSpeed = pMonsterList->pMonsters[monster_id - 1].uMovementSpeed; pActors[uNumActors].vInitialPosition.x = x; pActors[uNumActors].vPosition.x = x; pActors[uNumActors].uAttributes |= ACTOR_AGGRESSOR; pActors[uNumActors].pMonsterInfo.uTreasureType = 0; pActors[uNumActors].pMonsterInfo.uTreasureLevel = 0; pActors[uNumActors].pMonsterInfo.uTreasureDiceSides = 0; pActors[uNumActors].pMonsterInfo.uTreasureDiceRolls = 0; pActors[uNumActors].pMonsterInfo.uTreasureDropChance = 0; pActors[uNumActors].vInitialPosition.y = y; pActors[uNumActors].vPosition.y = y; pActors[uNumActors].vInitialPosition.z = z; pActors[uNumActors].vPosition.z = z; pActors[uNumActors].uTetherDistance = 256; pActors[uNumActors].uSectorID = v16; pActors[uNumActors].uGroup = 1; pActors[uNumActors].pMonsterInfo.uHostilityType = MonsterInfo::Hostility_Long; pActors[uNumActors].PrepareSprites(0); for ( int i = 0; i < 4; i++) pSoundList->LoadSound(pMonsterList->pMonsters[monster_id - 1].pSoundSampleIDs[i], 0); v12 = 0; do { v13 = pSoundList->LoadSound(v12 + word_4EE088_sound_ids[pMonsterStats->pInfos[monster_id].uSpell1ID], 1); v12++; } while ( v13 ); ++uNumActors; } } //----- (00426E10) -------------------------------------------------------- int stru319::which_player_to_attack(Actor *pActor) { signed int v2; // ebx@1 bool flag; // edi@37 int v22; // [sp+8h] [bp-140h]@3 int Victims_list[60]; // [sp+48h] [bp-100h]@48 int for_sex; // [sp+13Ch] [bp-Ch]@1 int for_race; // [sp+140h] [bp-8h]@1 int for_class; // [sp+144h] [bp-4h]@1 for_class = -1; for_race = -1; for_sex = -1; v2 = 0; if ( pActor->pMonsterInfo.uAttackPreference ) { for ( uint i = 0; i < 16; i++ ) { v22 = pActor->pMonsterInfo.uAttackPreference & (1 << i); if ( v22 ) { switch ( v22 ) { case 1: for_class = 0; break; case 2: for_class = 12; break; case 4: for_class = 16; break; case 8: for_class = 28; break; case 16: for_class = 24; break; case 32: for_class = 32; break; case 64: for_class = 20; break; case 128: for_class = 4; break; case 256: for_class = 8; break; case 512: for_sex = 0; break; case 1024: for_sex = 1; break; case 2048: for_race = 0; break; case 4096: for_race = 1; break; case 8192: for_race = 3; break; case 16384: for_race = 2; break; } v2 = 0; for ( uint j = 0; j < 4; ++j ) { flag = 0; if ( for_class != -1 && for_class == pPlayers[j + 1]->classType ) flag = true; if ( for_sex != -1 && for_sex == pPlayers[j + 1]->uSex ) flag = true; if ( for_race != -1 && for_race == pPlayers[j + 1]->GetRace() ) flag = true; if ( flag == true ) { if ( !(pPlayers[j + 1]->pConditions[Condition_Paralyzed] | pPlayers[j + 1]->pConditions[Condition_Unconcious] | pPlayers[j + 1]->pConditions[Condition_Dead] | pPlayers[j + 1]->pConditions[Condition_Pertified] | pPlayers[j + 1]->pConditions[Condition_Eradicated]) ) Victims_list[v2++] = j; } } } } if ( v2 ) return Victims_list[rand() % v2]; } for ( uint i = 0; i < 4; ++i ) { if ( !(pPlayers[i + 1]->pConditions[Condition_Paralyzed] | pPlayers[i + 1]->pConditions[Condition_Unconcious] | pPlayers[i + 1]->pConditions[Condition_Dead] | pPlayers[i + 1]->pConditions[Condition_Pertified] | pPlayers[i + 1]->pConditions[Condition_Eradicated]) ) Victims_list[v2++] = i; } if ( v2 ) return Victims_list[rand() % v2]; else return 0; } //----- (00427546) -------------------------------------------------------- int stru319::_427546(int a2) { int result; // eax@2 if (a2 >= 0) { if (a2 >= 1) result = (a2 >= 2) + 2; else result = 1; } else { result = 0; } return result; } //----- (0042F184) -------------------------------------------------------- int stru319::FindClosestActor(int pick_depth, int a3, int a4) { int v4; // edi@1 stru319 *v5; // esi@1 int v6; // eax@2 int v7; // eax@4 // int result; // eax@5 // int *v9; // edx@8 // signed int v10; // ebx@10 // int v11; // edi@11 //Actor *v12; // esi@12 //unsigned __int16 v13; // ax@12 // int v14; // eax@22 //char v15; // zf@30 // int v16; // esi@32 // int v17; // ecx@34 // stru319 *v18; // eax@39 // int v19; // edx@39 // int v20; // ecx@41 // unsigned __int16 v21; // ax@42 // unsigned int v22; // [sp+8h] [bp-24h]@11 //unsigned int v23; // [sp+Ch] [bp-20h]@7 stru319 *v24; // [sp+10h] [bp-1Ch]@1 // unsigned int v25; // [sp+14h] [bp-18h]@8 // int *v26; // [sp+18h] [bp-14h]@8 // int v27; // [sp+1Ch] [bp-10h]@10 // int *v28; // [sp+20h] [bp-Ch]@10 //unsigned int v29; // [sp+24h] [bp-8h]@7 // int v30; // [sp+28h] [bp-4h]@6 // int i; // [sp+38h] [bp+Ch]@33 // signed int v32; // [sp+3Ch] [bp+10h]@32 v4 = 0; v5 = this; v24 = this; //if ( pRenderer->pRenderD3D ) { v6 = a3 != 0; if (a4) LOBYTE(v6) = v6 | 8; v7 = pEngine->pVisInstance->PickClosestActor(OBJECT_Actor, pick_depth, v6, 657456, -1); if (v7 != -1) return (unsigned __int16)v7; else return 0; } /*else // software impl { v30 = 0; if ( pRenderer->pActiveZBuffer ) { if ( (signed int)viewparams->uScreen_topL_Y < (signed int)viewparams->uScreen_BttmR_Y ) { v9 = &pRenderer->pActiveZBuffer[viewparams->uScreen_topL_X + 640 * viewparams->uScreen_topL_Y]; v26 = &pRenderer->pActiveZBuffer[viewparams->uScreen_topL_X + 640 * viewparams->uScreen_topL_Y]; for ( v25 = viewparams->uScreen_BttmR_Y - viewparams->uScreen_topL_Y; v25; --v25 ) { if ( (signed int)viewparams->uScreen_topL_X < (signed int)viewparams->uScreen_BttmR_X ) { v28 = v9; v10 = v4; for ( v27 = viewparams->uScreen_BttmR_X - viewparams->uScreen_topL_X; v27; --v27 ) { v22 = *v28; v11 = *v28 & 0xFFFF; if (PID_TYPE(v11) == OBJECT_Actor) { if ( pActors[PID_ID(v11)].uAIState != Dead ) { if ( pActors[PID_ID(v11)].uAIState != Dying && pActors[PID_ID(v11)].uAIState != Removed && pActors[PID_ID(v11)].uAIState != Summoned && pActors[PID_ID(v11)].uAIState != Disabled && (!a3 || pActors[PID_ID(v11)].GetActorsRelation(0)) ) { if ( (!a4 || MonsterStats::BelongsToSupertype(pActors[PID_ID(v11)].pMonsterInfo.uID, MONSTER_SUPERTYPE_UNDEAD)) && v22 <= pick_depth << 16 ) { v14 = 0; if ( v10 > 0 ) { for ( v14; v14 < v30; ++v14 ) { if ( dword_50BDA0[v14] == v11 ) break; } } if ( v14 == v30 && v10 < 100 ) { ++v30; dword_50BC10[v10] = v22; dword_50BDA0[v10] = v11; ++v10; } } } } } ++v28; } v4 = v30; v5 = v24; } v9 = v26 + 640; v26 += 640; } } if ( v4 > 0 ) { v16 = (int)dword_50BC10.data(); for ( v32 = 1; v32 - 1 < v4; ++v32 ) { for ( i = v32; i < v4; ++i ) { v17 = dword_50BC10[i]; if ( dword_50BC10[i] < *(int *)v16 ) { dword_50BC10[i] = *(int *)v16; *(int *)v16 = v17; } } v16 += 4; } v5 = v24; if ( v4 > 0 ) { v18 = v24; for ( v19 = v4; v19; --v19 ) { *(int *)&v18->field_0 = (*(int *)&v18[(char *)dword_50BC10.data() - (char *)v24].field_0 >> 3) & 0x1FFF; v18 += 4; } } } v20 = 0; for ( *(int *)&v5[2000].field_0 = v4; v20 < v4; ++v20 ) { v21 = pActors[*(int *)&v5[4 * v20].field_0].uAIState; if ( v21 != 4 && v21 != 5 ) break; } if ( v20 != v4 ) { result = 8 * *(int *)&v5[4 * v20].field_0; LOBYTE(result) = result | 3; return result; } } } return 0;*/ } //----- (0042F4DA) -------------------------------------------------------- bool CheckActors_proximity() { signed int distance; // edi@1 int for_x; // ebx@5 int for_y; // [sp+Ch] [bp-10h]@5 int for_z; // [sp+10h] [bp-Ch]@5 distance = 5120; if ( uCurrentlyLoadedLevelType == LEVEL_Indoor) distance = 2560; if ( (signed int)uNumActors <= 0 ) return false; for ( uint i = 0; i < (signed int)uNumActors; ++i ) { for_x = abs(pActors[i].vInitialPosition.x - pParty->vPosition.x); for_y = abs(pActors[i].vInitialPosition.y - pParty->vPosition.y); for_z = abs(pActors[i].vInitialPosition.z - pParty->vPosition.z); if ( int_get_vector_length(for_x, for_y, for_z) < distance ) { if ( pActors[i].uAIState != Dead ) { if ( pActors[i].uAIState != Dying && pActors[i].uAIState != Removed && pActors[i].uAIState != Disabled && pActors[i].uAIState != Summoned && (pActors[i].ActorEnemy() || pActors[i].GetActorsRelation(0) ) ) return true; } } } return false; } //----- (00426A5A) -------------------------------------------------------- void Actor::LootActor() { signed int v2; // edi@1 unsigned __int8 v7; // al@30 char *v9; // [sp-4h] [bp-3Ch]@10 char *v10; // [sp-4h] [bp-3Ch]@31 char *v11; // [sp-4h] [bp-3Ch]@38 ItemGen Dst; // [sp+Ch] [bp-2Ch]@1 bool itemFound; // [sp+30h] [bp-8h]@1 int v14; // [sp+34h] [bp-4h]@1 pParty->sub_421B2C_PlaceInInventory_or_DropPickedItem(); Dst.Reset(); v2 = 0; itemFound = false; v14 = 0; if ( !ActorHasItem() ) { for (uchar i = 0; i < this->pMonsterInfo.uTreasureDiceRolls; i++ ) v14 += rand() % this->pMonsterInfo.uTreasureDiceSides + 1; if ( v14 ) { pParty->PartyFindsGold(v14, 0); viewparams->bRedrawGameUI = 1; } } else { if ( this->ActorHasItems[3].uItemID != 0 && this->ActorHasItems[3].GetItemEquipType() == EQUIP_GOLD ) { v14 = this->ActorHasItems[3].uSpecEnchantmentType; this->ActorHasItems[3].Reset(); if ( v14 ) { pParty->PartyFindsGold(v14, 0); viewparams->bRedrawGameUI = 1; } } } if ( this->uCarriedItemID ) { Dst.Reset(); Dst.uItemID = this->uCarriedItemID; v9 = pItemsTable->pItems[Dst.uItemID].pUnidentifiedName; if ( v14 ) sprintfex(pTmpBuf2.data(), pGlobalTXT_LocalizationStrings[490], v14, v9); else sprintfex(pTmpBuf2.data(), pGlobalTXT_LocalizationStrings[471], v9); ShowStatusBarString(pTmpBuf2.data(), 2); if ( Dst.GetItemEquipType() == 12 ) { Dst.uNumCharges = rand() % 6 + Dst.GetDamageMod() + 1; Dst.uMaxCharges = Dst.uNumCharges; } if ( pItemsTable->pItems[Dst.uItemID].uEquipType == 14 && Dst.uItemID != 220 ) Dst.uEnchantmentType = 2 * rand() % 4 + 2; pItemsTable->SetSpecialBonus(&Dst); if ( !pParty->AddItemToParty(&Dst) ) pParty->SetHoldingItem(&Dst); this->uCarriedItemID = 0; if ( this->ActorHasItems[0].uItemID ) { if ( !pParty->AddItemToParty(this->ActorHasItems) ) { pParty->sub_421B2C_PlaceInInventory_or_DropPickedItem(); pParty->SetHoldingItem(this->ActorHasItems); } this->ActorHasItems[0].Reset(); } if ( this->ActorHasItems[1].uItemID ) { if ( !pParty->AddItemToParty(&this->ActorHasItems[1]) ) { pParty->sub_421B2C_PlaceInInventory_or_DropPickedItem(); pParty->SetHoldingItem(&this->ActorHasItems[1]); } this->ActorHasItems[1].Reset(); } this->Remove(); return; } if ( this->ActorHasItem() ) { if ( this->ActorHasItems[3].uItemID ) { memcpy(&Dst, &this->ActorHasItems[3], sizeof(Dst)); this->ActorHasItems[3].Reset(); //v11 = pItemsTable->pItems[Dst.uItemID].pUnidentifiedName; if ( v14 ) sprintfex(pTmpBuf2.data(), pGlobalTXT_LocalizationStrings[490], v14, pItemsTable->pItems[Dst.uItemID].pUnidentifiedName); else sprintfex(pTmpBuf2.data(), pGlobalTXT_LocalizationStrings[471], pItemsTable->pItems[Dst.uItemID].pUnidentifiedName); ShowStatusBarString(pTmpBuf2.data(), 2); if ( !pParty->AddItemToParty(&Dst) ) pParty->SetHoldingItem(&Dst); itemFound = true; } } else { if ( rand() % 100 < this->pMonsterInfo.uTreasureDropChance && (v7 = this->pMonsterInfo.uTreasureLevel) != 0 ) { pItemsTable->GenerateItem(v7, this->pMonsterInfo.uTreasureType, &Dst); v10 = pItemsTable->pItems[Dst.uItemID].pUnidentifiedName; if ( v14 ) sprintfex(pTmpBuf2.data(), pGlobalTXT_LocalizationStrings[490], v14, v10);//Вы нашли ^I[%d] золот^L[ой;ых;ых] и предмет (%s)! else sprintfex(pTmpBuf2.data(), pGlobalTXT_LocalizationStrings[471], v10);//Вы нашли ^Pv[%s]! ShowStatusBarString(pTmpBuf2.data(), 2); if ( !pParty->AddItemToParty(&Dst) ) pParty->SetHoldingItem(&Dst); itemFound = true; } } if ( this->ActorHasItems[0].uItemID ) { if ( !pParty->AddItemToParty(this->ActorHasItems) ) { pParty->sub_421B2C_PlaceInInventory_or_DropPickedItem(); pParty->SetHoldingItem(this->ActorHasItems); itemFound = true; } this->ActorHasItems[0].Reset(); } if ( this->ActorHasItems[1].uItemID ) { if ( !pParty->AddItemToParty(&this->ActorHasItems[1]) ) { pParty->sub_421B2C_PlaceInInventory_or_DropPickedItem(); pParty->SetHoldingItem(&this->ActorHasItems[1]); itemFound = true; } this->ActorHasItems[1].Reset(); } if ( !itemFound || rand() % 100 < 90 )//for repeatedly get gold and item this->Remove(); } //----- (00427102) -------------------------------------------------------- bool Actor::_427102_IsOkToCastSpell( signed int a2 ) { switch(a2) { case SPELL_BODY_POWER_CURE: { if ( this->sCurrentHP >= (signed int)this->pMonsterInfo.uHP ) return false; return true; } case SPELL_LIGHT_DISPEL_MAGIC: { for (int i = 0; i < 20; i++) { if (pParty->pPartyBuffs[i].uExpireTime > 0) return true; } for ( int i = 1; i <= 4; i++ ) { for ( int j = 0; j < 22; j++ ) { if (pPlayers[i]->pPlayerBuffs[j].uExpireTime > 0) return true; } } return false; } case SPELL_LIGHT_DAY_OF_PROTECTION: { return this->pActorBuffs[ACTOR_BUFF_DAY_OF_PROTECTION].uExpireTime <= 0; break; } case SPELL_LIGHT_HOUR_OF_POWER: { return this->pActorBuffs[ACTOR_BUFF_HOUR_OF_POWER].uExpireTime <= 0; break; } case SPELL_DARK_PAIN_REFLECTION: { return this->pActorBuffs[ACTOR_BUFF_PAIN_REFLECTION].uExpireTime <= 0; break; } case SPELL_BODY_HAMMERHANDS: { return this->pActorBuffs[ACTOR_BUFF_PAIN_HAMMERHANDS].uExpireTime <= 0; break; } case SPELL_FIRE_HASTE: { return this->pActorBuffs[ACTOR_BUFF_HASTE].uExpireTime <= 0; break; } case SPELL_AIR_SHIELD: { return this->pActorBuffs[ACTOR_BUFF_SHIELD].uExpireTime <= 0; break; } case SPELL_EARTH_STONESKIN: { return this->pActorBuffs[ACTOR_BUFF_STONESKIN].uExpireTime <= 0; break; } case SPELL_SPIRIT_BLESS: { return this->pActorBuffs[ACTOR_BUFF_BLESS].uExpireTime <= 0; break; } case SPELL_SPIRIT_FATE: { return this->pActorBuffs[ACTOR_BUFF_FATE].uExpireTime <= 0; break; } case SPELL_SPIRIT_HEROISM: { return this->pActorBuffs[ACTOR_BUFF_HEROISM].uExpireTime <= 0; break; } default: return true; } } //----- (0042704B) -------------------------------------------------------- ABILITY_INDEX Actor::special_ability_use_check( int a2 ) { signed int okToCastSpell1; // ebx@5 signed int okToCastSpell2; // edi@5 if ( this->pMonsterInfo.uSpecialAbilityType == 2 && this->pMonsterInfo.uSpecialAbilityDamageDiceBonus < 3 && rand() % 100 < 5 ) this->SummonMinion(a2); okToCastSpell1 = this->_427102_IsOkToCastSpell(this->pMonsterInfo.uSpell1ID); okToCastSpell2 = this->_427102_IsOkToCastSpell(this->pMonsterInfo.uSpell2ID); if ( okToCastSpell1 && this->pMonsterInfo.uSpell1UseChance && rand() % 100 < this->pMonsterInfo.uSpell1UseChance ) return ABILITY_SPELL1; if ( okToCastSpell2 && this->pMonsterInfo.uSpell2UseChance && rand() % 100 < this->pMonsterInfo.uSpell2UseChance ) return ABILITY_SPELL2; if (this->pMonsterInfo.uAttack2Chance && rand() % 100 < this->pMonsterInfo.uAttack2Chance) return ABILITY_ATTACK2; return ABILITY_ATTACK1; } //----- (004273BB) -------------------------------------------------------- bool Actor::_4273BB_DoesHitOtherActor( Actor *defender, int a3, int a4 ) { signed int v6; // ebx@1 signed int v7; // esi@1 int armorSum; // ebx@10 signed int a2a; // [sp+18h] [bp+Ch]@1 v6 = defender->pMonsterInfo.uAC; v7 = 0; a2a = 0; if ( defender->pActorBuffs[ACTOR_BUFF_SOMETHING_THAT_HALVES_AC].uExpireTime > 0 ) v6 /= 2; if ( defender->pActorBuffs[ACTOR_BUFF_HOUR_OF_POWER].uExpireTime > 0 ) v7 = defender->pActorBuffs[ACTOR_BUFF_HOUR_OF_POWER].uPower; if ( defender->pActorBuffs[ACTOR_BUFF_STONESKIN].uExpireTime > 0 && defender->pActorBuffs[ACTOR_BUFF_STONESKIN].uPower > v7 ) v7 = defender->pActorBuffs[ACTOR_BUFF_STONESKIN].uPower; armorSum = v7 + v6; if ( this->pActorBuffs[ACTOR_BUFF_HOUR_OF_POWER].uExpireTime > 0 ) a2a = this->pActorBuffs[ACTOR_BUFF_HOUR_OF_POWER].uPower; if ( this->pActorBuffs[ACTOR_BUFF_BLESS].uExpireTime > 0 && this->pActorBuffs[ACTOR_BUFF_BLESS].uPower > a2a ) a2a = this->pActorBuffs[ACTOR_BUFF_BLESS].uPower; if ( this->pActorBuffs[ACTOR_BUFF_FATE].uExpireTime > 0 ) { a2a += this->pActorBuffs[ACTOR_BUFF_FATE].uPower; this->pActorBuffs[ACTOR_BUFF_FATE].Reset(); } return rand() % (armorSum + 2 * this->pMonsterInfo.uLevel + 10) + a2a + 1 > armorSum + 5; } //----- (004274AD) -------------------------------------------------------- bool Actor::ActorHitOrMiss(Player *pPlayer) { signed int v3; // edi@1 signed int v4; // esi@8 int v5; // esi@8 v3 = 0; if ( this->pActorBuffs[ACTOR_BUFF_HOUR_OF_POWER].uExpireTime > 0 ) v3 = this->pActorBuffs[ACTOR_BUFF_HOUR_OF_POWER].uPower; if ( this->pActorBuffs[ACTOR_BUFF_BLESS].uExpireTime > 0 && this->pActorBuffs[ACTOR_BUFF_BLESS].uPower > v3 ) v3 = this->pActorBuffs[ACTOR_BUFF_BLESS].uPower; if ( this->pActorBuffs[ACTOR_BUFF_FATE].uExpireTime > 0 ) { v3 += this->pActorBuffs[ACTOR_BUFF_FATE].uPower; this->pActorBuffs[ACTOR_BUFF_FATE].Reset(); } v4 = pPlayer->GetActualAC() + 2 * this->pMonsterInfo.uLevel + 10; v5 = rand() % v4 + 1; return (v3 + v5 > pPlayer->GetActualAC() + 5); } //----- (0042756B) -------------------------------------------------------- int Actor::CalcMagicalDamageToActor(DAMAGE_TYPE dmgType, signed int incomingDmg) { int v4; // edx@1 int v5; // ecx@1 signed int v6; // eax@4 signed int result; // eax@17 signed int v8; // esi@18 v4 = 0; v5 = 0; if ( this->pActorBuffs[ACTOR_BUFF_HOUR_OF_POWER].uExpireTime > 0 ) v5 = this->pActorBuffs[ACTOR_BUFF_HOUR_OF_POWER].uPower; switch ( dmgType ) { case DMGT_FIRE: v6 = this->pMonsterInfo.uResFire; v4 = v5; break; case DMGT_ELECTR: v6 = this->pMonsterInfo.uResAir; v4 = v5; break; case DMGT_COLD: v6 = this->pMonsterInfo.uResWater; v4 = v5; break; case DMGT_EARTH: v6 = this->pMonsterInfo.uResEarth; v4 = v5; break; case DMGT_PHISYCAL: v6 = this->pMonsterInfo.uResPhysical; break; case DMGT_SPIRIT: v6 = this->pMonsterInfo.uResSpirit; break; case DMGT_MIND: v6 = this->pMonsterInfo.uResMind; v4 = v5; break; case DMGT_BODY: v6 = this->pMonsterInfo.uResBody; v4 = v5; break; case DMGT_LIGHT: v6 = this->pMonsterInfo.uResLight; break; case DMGT_DARK: v6 = this->pMonsterInfo.uResDark; break; default: v6 = 0; break; } if ( v6 < 200 ) { v8 = v4 + v6 + 30; for (int i = 0; i < 4; i++) { if ( rand() % v8 < 30 ) break; incomingDmg /= 2; } result = incomingDmg; } else result = 0; return result; } //----- (00427662) -------------------------------------------------------- bool Actor::DoesDmgTypeDoDamage(DAMAGE_TYPE uType) { signed int resist; // esi@2 bool result; // eax@13 switch ( uType ) { case 0: resist = this->pMonsterInfo.uResFire; break; case 1: resist = this->pMonsterInfo.uResAir; break; case 2: resist = this->pMonsterInfo.uResWater; break; case 3: resist = this->pMonsterInfo.uResEarth; break; case 4: resist = this->pMonsterInfo.uResPhysical; break; case 6: resist = this->pMonsterInfo.uResSpirit; break; case 7: resist = this->pMonsterInfo.uResMind; case 8: resist = this->pMonsterInfo.uResBody; break; case 9: resist = this->pMonsterInfo.uResLight; break; case 10: resist = this->pMonsterInfo.uResDark; break; default: return 1; } if ( resist < 200 ) result = rand() % ((this->pMonsterInfo.uLevel >> 2) + resist + 30) < 30; else result = 0; return result; } //----- (00448A98) -------------------------------------------------------- void __fastcall ToggleActorGroupFlag(unsigned int uGroupID, unsigned int uFlag, unsigned int bToggle) { if ( uGroupID ) { if ( bToggle ) { for ( uint i = 0; i < (unsigned int)uNumActors; ++i ) { if ( pActors[i].uGroup == uGroupID ) { pActors[i].uAttributes |= uFlag; if ( uFlag == 0x10000 ) { pActors[i].uAIState = Disabled; pActors[i].UpdateAnimation(); } } } } else { for ( uint i = 0; i < (unsigned int)uNumActors; ++i ) { if ( pActors[i].uGroup == uGroupID ) { if ( uFlag == 0x10000 ) { if ( pActors[i].uAIState != Dead ) { if ( pActors[i].uAIState != 4 && pActors[i].uAIState != 11 ) pActors[i].uAIState = Standing; } } LODWORD(pActors[i].uAttributes) &= ~uFlag; } } } } } //----- (004014E6) -------------------------------------------------------- void Actor::MakeActorAIList_ODM() { int v1; // eax@4 unsigned int v7; // ST20_4@10 int distance; // edi@10 int v10; // ebx@14 int v21; // [sp+Ch] [bp-14h]@4 int v22; // [sp+10h] [bp-10h]@4 pParty->uFlags &= 0xFFFFFFCF;//~0x30 ai_arrays_size = 0; for (uint i = 0; i < uNumActors; ++i) { Actor* actor = &pActors[i]; actor->ResetAlive();//~0x400 if (!actor->CanAct()) { actor->ResetActive(); continue; } v22 = abs(pParty->vPosition.z - actor->vPosition.z); v21 = abs(pParty->vPosition.y - actor->vPosition.y); v1 = abs(pParty->vPosition.x - actor->vPosition.x); v7 = int_get_vector_length(v22, v21, v1); distance = v7 - actor->uActorRadius; if ( distance < 0 ) distance = 0; if (distance < 5632) { actor->ResetHostile(); if ( actor->ActorEnemy() || actor->GetActorsRelation(0) ) { //v11 = (pParty->uFlags & 0x10) == 0; actor->uAttributes |= ACTOR_HOSTILE; if (distance < 5120 ) pParty->SetYellowAlert(); if (distance < 307) pParty->SetRedAlert(); } actor->uAttributes |= ACTOR_ACTIVE; ai_near_actors_distances[ai_arrays_size] = distance; ai_near_actors_ids[ai_arrays_size++] = i; } else actor->ResetActive(); } /* result = v27; if ( v27 > 0 ) { v14 = 0; v15 = 1; v26 = 1; do { while ( 1 ) { v24 = v15; if ( v15 >= result ) break; v16 = ai_near_actors_distances[v14]; if ( v16 > ai_near_actors_distances[v15] ) { v17 = &ai_near_actors_ids[v15]; v18 = ai_near_actors_ids[v14]; ai_near_actors_ids[v14] = *v17; *v17 = v18; v15 = v24; ai_near_actors_distances[v14] = ai_near_actors_distances[v24]; ai_near_actors_distances[v24] = v16; } result = v27; ++v15; } ++v14; v15 = v26 + 1; v26 = v15; } while ( v15 - 1 < result ); }*/ for (uint i = 0; i < ai_arrays_size; ++i) for (uint j = 0; j < i; ++j) if (ai_near_actors_distances[j] > ai_near_actors_distances[i]) { int tmp = ai_near_actors_distances[j]; ai_near_actors_distances[j] = ai_near_actors_distances[i]; ai_near_actors_distances[i] = tmp; tmp = ai_near_actors_ids[j]; ai_near_actors_ids[j] = ai_near_actors_ids[i]; ai_near_actors_ids[i] = tmp; } if (ai_arrays_size > 30) ai_arrays_size = 30; for (uint i = 0; i < ai_arrays_size; ++i) pActors[ai_near_actors_ids[i]].uAttributes |= ACTOR_ALIVE;//0x400 } //----- (004016FA) -------------------------------------------------------- int Actor::MakeActorAIList_BLV() { int v1; // eax@4 int distance; // edi@10 int v13; // edx@24 int v15; // ebx@26 unsigned int v17; // esi@27 int v18; // ecx@31 signed int v19; // edi@31 signed int v25; // eax@40 int j; // edi@45 int v30; // eax@48 int v37; // [sp+Ch] [bp-18h]@1 int v38; // [sp+10h] [bp-14h]@4 int v39; // [sp+14h] [bp-10h]@4 int i; // [sp+18h] [bp-Ch]@31 uint v45; // [sp+20h] [bp-4h]@1 // __debugbreak(); // refactor for blv ai pParty->uFlags &= 0xFFFFFFCF;//~0x30 v37 = pIndoor->GetSector(pParty->vPosition.x, pParty->vPosition.y, pParty->vPosition.z); v45 = 0; for ( uint i = 0; i < (signed int)uNumActors; ++i ) { pActors[i].ResetAlive();//~0x0400 if ( !pActors[i].CanAct() ) { pActors[i].ResetActive(); continue; } v1 = abs(pParty->vPosition.x - pActors[i].vPosition.x); v38 = abs(pParty->vPosition.y - pActors[i].vPosition.y); v39 = abs(pParty->vPosition.z - pActors[i].vPosition.z); distance = int_get_vector_length(v39, v38, v1) - pActors[i].uActorRadius; if ( distance < 0 ) distance = 0; if ( distance < 10240 ) { pActors[i].ResetHostile();//~0x01000000 if ( pActors[i].ActorEnemy() || pActors[i].GetActorsRelation(0) ) { pActors[i].uAttributes |= ACTOR_HOSTILE; if ( !(pParty->uFlags & 0x10) && (double)distance < 307.2 ) pParty->SetRedAlert(); if ( !(pParty->uFlags & 0x20) && distance < 5120 ) pParty->SetYellowAlert(); } ai_near_actors_distances[v45] = distance; ai_near_actors_ids[v45] = i; v45++; } else pActors[i].ResetActive(); } v13 = 0; if ( v45 > 0 ) { for ( uint i = 1; i < v45; i++ ) { for ( uint j = 1; j < v45; ++j ) { v15 = ai_near_actors_distances[v13]; if ( ai_near_actors_distances[v13] > ai_near_actors_distances[j] ) { v17 = ai_near_actors_ids[v13]; ai_near_actors_ids[v13] = ai_near_actors_ids[j]; ai_near_actors_ids[j] = v17; ai_near_actors_distances[v13] = ai_near_actors_distances[j]; ai_near_actors_distances[j] = v15; } } ++v13; } } v18 = 0; v19 = 0; for ( i = 0; i < v45; i++ ) { if ( pActors[ai_near_actors_ids[i]].ActorNearby() || sub_4070EF_prolly_detect_player(PID(OBJECT_Actor,ai_near_actors_ids[i]), 4) ) { pActors[ai_near_actors_ids[i]].uAttributes |= ACTOR_NEARBY; ai_array_4F6638_actor_ids[v19] = ai_near_actors_ids[i]; ai_array_4F5E68[v19++] = ai_near_actors_distances[i]; if ( v19 >= 30 ) break; } } ai_arrays_size = v19; if ( (signed int)uNumActors > 0 ) { for ( uint i = 0; i < (signed int)uNumActors; ++i ) { if ( pActors[i].CanAct() && pActors[i].uSectorID == v37 ) { v25 = 0; if ( v19 <= 0 ) { pActors[i].uAttributes |= ACTOR_ACTIVE; ai_array_4F6638_actor_ids[ai_arrays_size++] = i; } else { while ( ai_array_4F6638_actor_ids[v25] != i ) { ++v25; if ( v25 >= v19 ) { pActors[i].uAttributes |= ACTOR_ACTIVE; ai_array_4F6638_actor_ids[ai_arrays_size++] = i; break; } } } } } } for ( j = 0; j < v45; ++j ) { if ( pActors[ai_near_actors_ids[j]].uAttributes & 0xC000 && pActors[ai_near_actors_ids[j]].CanAct() ) { v30 = 0; if ( ai_arrays_size <= 0 ) ai_array_4F6638_actor_ids[ai_arrays_size++] = ai_near_actors_ids[j]; else { while ( ai_array_4F6638_actor_ids[v30] != ai_near_actors_ids[j] ) { ++v30; if ( v30 >= ai_arrays_size ) { ai_array_4F6638_actor_ids[ai_arrays_size++] = ai_near_actors_ids[j]; break; } } } } } if ( ai_arrays_size > 30 ) ai_arrays_size = 30; memcpy(ai_near_actors_ids.data(), ai_array_4F6638_actor_ids.data(), 4 * ai_arrays_size); memcpy(ai_near_actors_distances.data(), ai_array_4F5E68.data(), 4 * ai_arrays_size); for ( uint i = 0; i < ai_arrays_size; i++ ) pActors[ai_near_actors_ids[i]].uAttributes |= ACTOR_ALIVE;//0x400 return ai_arrays_size; } //----- (004070EF) -------------------------------------------------------- bool __fastcall sub_4070EF_prolly_detect_player(unsigned int uObjID, unsigned int uObj2ID) { signed int v2; // eax@1 int obj1_sector; // eax@4 float v8; // ST24_4@5 signed int v12; // eax@7 int obj2_z; // edi@11 int obj2_x; // esi@11 int obj2_sector; // eax@13 float v20; // ST24_4@14 int dist_x; // ebx@16 signed int dist_3d; // ecx@16 int v25; // eax@18 BLVFace *v29; // ebx@32 Vec3_short_ *v30; // esi@32 int v31; // eax@32 int v32; // ST50_4@44 int v33; // ST54_4@44 int v34; // eax@44 signed int v38; // esi@45 __int16 next_sector; // bx@58 int v47; // [sp+18h] [bp-50h]@20 int v48; // [sp+1Ch] [bp-4Ch]@20 int v49; // [sp+20h] [bp-48h]@20 int dist_z; // [sp+24h] [bp-44h]@16 signed int higher_z; // [sp+24h] [bp-44h]@27 signed int lower_z; // [sp+28h] [bp-40h]@26 signed int higher_y; // [sp+2Ch] [bp-3Ch]@23 signed int lower_y; // [sp+30h] [bp-38h]@22 signed int higher_x; // [sp+34h] [bp-34h]@21 signed int lower_x; // [sp+38h] [bp-30h]@20 signed int sectors_visited; // [sp+3Ch] [bp-2Ch]@28 int v58; // [sp+44h] [bp-24h]@50 int v59; // [sp+48h] [bp-20h]@44 int obj2_y; // [sp+50h] [bp-18h]@11 int obj1_x; // [sp+58h] [bp-10h]@4 int obj1_y; // [sp+5Ch] [bp-Ch]@4 int obj1_z; // [sp+60h] [bp-8h]@4 int current_sector; // [sp+64h] [bp-4h]@7 int dist_y; int v70; v2 = PID_ID(uObjID); switch( PID_TYPE(uObjID) ) { case OBJECT_Decoration: obj1_x = pLevelDecorations[v2].vPosition.x; obj1_y = pLevelDecorations[v2].vPosition.y; obj1_z = pLevelDecorations[v2].vPosition.z; obj1_sector = pIndoor->GetSector(obj1_x, obj1_y, obj1_z); break; case OBJECT_Actor: obj1_x = pActors[v2].vPosition.x; obj1_y = pActors[v2].vPosition.y; v8 = (double)pActors[v2].uActorHeight * 0.69999999; //v9 = v8 + 6.7553994e15; //obj1_z = LODWORD(v9) + pActors[v2].vPosition.z; obj1_z = (int)v8 + pActors[v2].vPosition.z; obj1_sector = pActors[v2].uSectorID; break; case OBJECT_Item: obj1_x = pSpriteObjects[v2].vPosition.x; obj1_y = pSpriteObjects[v2].vPosition.y; obj1_z = pSpriteObjects[v2].vPosition.z; obj1_sector = pSpriteObjects[v2].uSectorID; break; default: return 0; } v12 = PID_ID(uObj2ID); switch( PID_TYPE(uObj2ID) ) { case OBJECT_Decoration: obj2_z = pLevelDecorations[v12].vPosition.z; obj2_x = pLevelDecorations[v12].vPosition.x; obj2_y = pLevelDecorations[v12].vPosition.y; obj2_sector = pIndoor->GetSector(obj2_x, obj2_y, obj2_z); break; case OBJECT_Player: obj2_x = pParty->vPosition.x; obj2_z = pParty->sEyelevel + pParty->vPosition.z; obj2_y = pParty->vPosition.y; obj2_sector = pIndoor->GetSector(pParty->vPosition.x, pParty->vPosition.y, pParty->sEyelevel + pParty->vPosition.z); break; case OBJECT_Actor: obj2_y = pActors[v12].vPosition.y; obj2_x = pActors[v12].vPosition.x; v20 = (double)pActors[v12].uActorHeight * 0.69999999; //v21 = v20 + 6.7553994e15; //obj2_z = LODWORD(v21) + pActors[v12].vPosition.z; obj2_z = (int)v20 + pActors[v12].vPosition.z; obj2_sector = pActors[v12].uSectorID; break; case OBJECT_Item: obj2_x = pSpriteObjects[v12].vPosition.x; obj2_z = pSpriteObjects[v12].vPosition.z; obj2_y = pSpriteObjects[v12].vPosition.y; obj2_sector = pSpriteObjects[v12].uSectorID; break; default: return 0; } dist_x = obj2_x - obj1_x; dist_z = obj2_z - obj1_z; dist_y = obj2_y - obj1_y; dist_3d = integer_sqrt(dist_x * dist_x + dist_y * dist_y + dist_z * dist_z); //range check if ( dist_3d > 5120 ) return 0; if ( uCurrentlyLoadedLevelType == LEVEL_Outdoor) return 1; v25 = 65536; if ( dist_3d ) v25 = 65536 / dist_3d; v49 = dist_x * v25; v47 = dist_z * v25; v48 = dist_y * v25; if ( obj1_x < obj2_x ) { lower_x = obj1_x; higher_x = obj2_x; } else { lower_x = obj2_x; higher_x = obj1_x; } if ( obj1_y < obj2_y ) { lower_y = obj1_y; higher_y = obj2_y; } else { lower_y = obj2_y; higher_y = obj1_y; } if ( obj1_z < obj2_z ) { lower_z = obj1_z; higher_z = obj2_z; } else { lower_z = obj2_z; higher_z = obj1_z; } sectors_visited = 0; //monster in same sector with player if ( obj1_sector == obj2_sector ) return 1; //search starts from monster current_sector = obj1_sector; for( int current_portal = 0; current_portal < pIndoor->pSectors[current_sector].uNumPortals; current_portal++ ) { v29 = &pIndoor->pFaces[pIndoor->pSectors[current_sector].pPortals[current_portal]]; v30 = &pIndoor->pVertices[*v29->pVertexIDs]; v31 = v29->pFacePlane_old.vNormal.z * (v30->z - obj1_z) + v29->pFacePlane_old.vNormal.y * (v30->y - obj1_y) + v29->pFacePlane_old.vNormal.x * (v30->x - obj1_x); if ( current_sector != v29->uSectorID ) v31 = -v31; if ( v31 >= 0 && v30->x != obj1_x && v30->y != obj1_y && v30->z != obj1_z) continue; if( lower_x > v29->pBounding.x2 || higher_x < v29->pBounding.x1 || lower_y > v29->pBounding.y2 || higher_y < v29->pBounding.y1 || lower_z > v29->pBounding.z2 || higher_z < v29->pBounding.z1 ) { continue; } v32 = fixpoint_mul(v29->pFacePlane_old.vNormal.x,v49); v34 = fixpoint_mul(v29->pFacePlane_old.vNormal.y,v48); v33 = fixpoint_mul(v29->pFacePlane_old.vNormal.z,v47); v59 = v32 + v33 + v34; if ( v59 ) { v70 = v29->pFacePlane_old.dist + obj1_z * v29->pFacePlane_old.vNormal.z + obj1_x * v29->pFacePlane_old.vNormal.x + obj1_y * v29->pFacePlane_old.vNormal.y; v38 = -v70; // if ( v59 <= 0 ^ v70 <= 0 ) /* TEMPORARY if ( v59 <= 0 && v70 <= 0 ) { continue; } if ( !(v59 <= 0 && v70 <= 0) ) { continue; } */ if( abs(v38) >> 14 > abs(v59) ) continue; v58 = fixpoint_div(v38,v59); if( v58 < 0 ) continue; if(!sub_4075DB(obj1_x + ((fixpoint_mul(v49,v58) + 32768) >> 16), obj1_y + ((fixpoint_mul(v48,v58) + 32768) >> 16), obj1_z + ((fixpoint_mul(v47,v58) + 32768) >> 16), v29) ) { continue; } //if there is no next sector turn back if ( v29->uSectorID == current_sector ) next_sector = v29->uBackSectorID; else next_sector = v29->uSectorID; //no more portals, quit if ( next_sector == current_sector ) break; ++sectors_visited; current_sector = next_sector; //found player, quit if ( next_sector == obj2_sector ) return 1; current_sector = next_sector; //did we hit limit for portals? //does the next room have portals? if ( sectors_visited < 30 && pIndoor->pSectors[current_sector].uNumPortals > 0) { current_portal=-1; continue; } else break; } } //did we stop in the sector where player is? if ( current_sector != obj2_sector ) return 0; return 1; } //----- (00450B0A) -------------------------------------------------------- bool __fastcall SpawnActor(unsigned int uMonsterID) { unsigned int v1; // ebx@1 bool result; // eax@2 unsigned int v6; // ecx@5 Actor actor; // [sp+4h] [bp-350h]@5 Vec3_int_ pOut; // [sp+348h] [bp-Ch]@5 v1 = uMonsterID; if ( uNumActors == 499 ) result = 0; else { if ( (signed int)uMonsterID >= (signed int)pMonsterList->uNumMonsters ) v1 = 0; memset(&actor, 0, sizeof(Actor)); strcpy(actor.pActorName, pMonsterStats->pInfos[v1 + 1].pName); actor.sCurrentHP = LOWORD(pMonsterStats->pInfos[v1 + 1].uHP); memcpy(&actor.pMonsterInfo, &pMonsterStats->pInfos[v1 + 1], sizeof(MonsterInfo)); actor.word_000086_some_monster_id = v1 + 1; actor.uActorRadius = pMonsterList->pMonsters[v1].uMonsterRadius; actor.uActorHeight = pMonsterList->pMonsters[v1].uMonsterHeight; actor.uMovementSpeed = pMonsterList->pMonsters[v1].uMovementSpeed; Vec3_int_::Rotate(200, pParty->sRotationY, 0, pParty->vPosition, &pOut.x, &pOut.z, &pOut.y); actor.vInitialPosition.x = pOut.x; actor.vPosition.x = pOut.x; actor.uTetherDistance = 256; actor.vInitialPosition.y = LOWORD(pOut.z); actor.vPosition.y = LOWORD(pOut.z); actor.vInitialPosition.z = LOWORD(pOut.y); actor.vPosition.z = LOWORD(pOut.y); pSprites_LOD->DeleteSomeSprites(); pPaletteManager->ResetNonTestLocked(); v6 = uNumActors - 1; if ( dword_5C6DF8 == 1 ) { dword_5C6DF8 = 0; v6 = uNumActors++; } memcpy(&pActors[v6], &actor, sizeof(Actor)); pActors[v6].PrepareSprites(1); result = 1; } return result; } // 5C6DF8: using guessed type int dword_5C6DF8; //----- (0044FA4C) -------------------------------------------------------- signed int __fastcall sub_44FA4C_spawn_light_elemental(int a1, int a2, int a3) { signed int result; // eax@13 int v10; // ebx@16 const char *v15; // [sp-4h] [bp-24h]@2 unsigned int uFaceID; // [sp+8h] [bp-18h]@16 int v19; // [sp+Ch] [bp-14h]@16 size_t v20; // [sp+10h] [bp-10h]@6 int v21; // [sp+14h] [bp-Ch]@14 unsigned int v23; // [sp+1Ch] [bp-4h]@6 if ( a2 == 4 ) v15 = "Elemental Light C"; else if ( a2 == 3 ) v15 = "Elemental Light B"; else v15 = "Elemental Light A"; v23 = pMonsterList->GetMonsterIDByName(v15); v20 = 0; for ( v20; v20 < uNumActors; v20++ ) { if ( pActors[v20].uAIState == Removed ) break; } result = uNumActors + 1; if ( v20 != uNumActors || result < 500 ) { v21 = 0; if ( uCurrentlyLoadedLevelType == LEVEL_Indoor ) v21 = pIndoor->GetSector(pParty->vPosition.x, pParty->vPosition.y, pParty->vPosition.z); v19 = (((uCurrentlyLoadedLevelType != LEVEL_Outdoor) - 1) & 0x40) + 64; pActors[v20].Reset(); strcpy(pActors[v20].pActorName, pMonsterStats->pInfos[v23 + 1].pName); pActors[v20].sCurrentHP = pMonsterStats->pInfos[v23 + 1].uHP; memcpy(&pActors[v20].pMonsterInfo, &pMonsterStats->pInfos[v23 + 1], sizeof(MonsterInfo)); pActors[v20].word_000086_some_monster_id = v23 + 1; pActors[v20].uActorRadius = pMonsterList->pMonsters[v23].uMonsterRadius; pActors[v20].uActorHeight = pMonsterList->pMonsters[v23].uMonsterHeight; pActors[v20].pMonsterInfo.uTreasureDiceRolls = 0; pActors[v20].pMonsterInfo.uTreasureType = 0; pActors[v20].pMonsterInfo.uExp = 0; pActors[v20].uMovementSpeed = pMonsterList->pMonsters[v23].uMovementSpeed; v10 = rand() % 2048; pActors[v20].vInitialPosition.x = pParty->vPosition.x + fixpoint_mul(stru_5C6E00->Cos(v10), v19); pActors[v20].vPosition.x = pActors[v20].vInitialPosition.x; pActors[v20].vInitialPosition.y = pParty->vPosition.y + fixpoint_mul(stru_5C6E00->Sin(v10), v19); pActors[v20].vPosition.y = pActors[v20].vInitialPosition.y; pActors[v20].vInitialPosition.z = pParty->vPosition.z; pActors[v20].vPosition.z = pActors[v20].vInitialPosition.z; pActors[v20].uTetherDistance = 256; pActors[v20].uSectorID = v21; pActors[v20].PrepareSprites(0); pActors[v20].pMonsterInfo.uHostilityType = MonsterInfo::Hostility_Friendly; pActors[v20].uAlly = 9999; pActors[v20].uGroup = 0; pActors[v20].uCurrentActionTime = 0; pActors[v20].uAIState = Summoned; pActors[v20].uCurrentActionLength = 256; pActors[v20].UpdateAnimation(); result = pIndoor->GetSector(pActors[v20].vPosition.x, pActors[v20].vPosition.y, pActors[v20].vPosition.z); if ( uCurrentlyLoadedLevelType == LEVEL_Outdoor || result == v21 && (result = BLV_GetFloorLevel(pActors[v20].vPosition.x, pActors[v20].vPosition.y, pActors[v20].vPosition.z, result, &uFaceID), result != -30000) && (result = abs(result - pParty->vPosition.z), result <= 1024) ) { if ( v20 == uNumActors ) ++uNumActors; pActors[v20].uSummonerID = PID(OBJECT_Player, a1); result = pActors[v20].pActorBuffs[ACTOR_BUFF_SUMMONED].Apply(pParty->uTimePlayed + (a3 * 128) / 30.0f, a2, a1, 0, 0); } } return result; } //----- (0044F57C) -------------------------------------------------------- void SpawnEncounter(MapInfo *pMapInfo, SpawnPointMM7 *spawn, int a3, int a4, int a5) { int v7; // eax@2 char v8; // zf@5 int v12; // edx@9 int v18; // esi@31 Actor *pMonster; // esi@35 int v23; // edx@36 signed int v24; // edi@36 int v25; // ecx@36 MonsterDesc *v27; // edi@48 signed int v28; // eax@48 int v32; // eax@50 int v37; // eax@51 int v38; // eax@52 int v39; // edi@52 std::string v40; // [sp-18h] [bp-100h]@60 const char *v44; // [sp-8h] [bp-F0h]@13 char *pTexture; // [sp-4h] [bp-ECh]@9 char Str[32]; // [sp+Ch] [bp-DCh]@60 char Str2[120]; // [sp+2Ch] [bp-BCh]@29 unsigned int uFaceID; // [sp+A4h] [bp-44h]@52 MonsterInfo *Src; // [sp+A8h] [bp-40h]@50 int v50; // [sp+ACh] [bp-3Ch]@47 char Source[32]; // [sp+B0h] [bp-38h]@20 int v52; // [sp+D0h] [bp-18h]@34 int v53; // [sp+D4h] [bp-14h]@34 int pSector; // [sp+D8h] [bp-10h]@32 int pPosX; // [sp+DCh] [bp-Ch]@32 int v56; // [sp+E0h] [bp-8h]@8 int v57; // [sp+E4h] [bp-4h]@1 //auto a2 = spawn; v57 = 0; //v5 = pMapInfo; //v6 = spawn; if ( uCurrentlyLoadedLevelType == LEVEL_Indoor ) v7 = pOutdoor->ddm.field_C_alert; else if (uCurrentlyLoadedLevelType == LEVEL_Outdoor) v7 = pIndoor->dlv.field_C_alert; else v7 = 0; if (v7) v8 = (spawn->uAttributes & 1) == 0; else v8 = (spawn->uAttributes & 1) == 1; if (v8) return; //result = (void *)(spawn->uIndex - 1); v56 = 1; switch (spawn->uIndex - 1) { case 0: //v9 = pMapInfo->uEncounterMonster1AtLeast; //v10 = rand(); //v11 = pMapInfo->uEncounterMonster1AtMost; //pTexture = pMapInfo->pEncounterMonster1Texture; v12 = rand() % (pMapInfo->uEncounterMonster1AtMost - pMapInfo->uEncounterMonster1AtLeast + 1); //v13 = pMapInfo->Dif_M1; v57 = pMapInfo->Dif_M1; v56 = pMapInfo->uEncounterMonster1AtLeast + v12; strcpy(Source, pMapInfo->pEncounterMonster1Texture); break; case 3: //pTexture = pMapInfo->pEncounterMonster1Texture; //v44 = "%s A"; sprintf(Source, "%s A", pMapInfo->pEncounterMonster1Texture); break; case 4: //pTexture = pMapInfo->pEncounterMonster2Texture; //v44 = "%s A"; sprintf(Source, "%s A", pMapInfo->pEncounterMonster2Texture); break; case 5: //pTexture = pMapInfo->pEncounterMonster3Texture; //v44 = "%s A"; sprintf(Source, "%s A", pMapInfo->pEncounterMonster3Texture); break; case 1: //v9 = pMapInfo->uEncounterMonster2AtLeast; //v14 = rand(); //v15 = pMapInfo->uEncounterMonster2AtMost; //pTexture = pMapInfo->pEncounterMonster2Texture; v12 = rand() % (pMapInfo->uEncounterMonster2AtMost - pMapInfo->uEncounterMonster2AtLeast + 1); //v13 = pMapInfo->Dif_M2; v57 = pMapInfo->Dif_M2; v56 = pMapInfo->uEncounterMonster2AtLeast + v12; strcpy(Source, pMapInfo->pEncounterMonster2Texture); break; case 6: //pTexture = pMapInfo->pEncounterMonster1Texture; //v44 = "%s B"; sprintf(Source, "%s B", pMapInfo->pEncounterMonster1Texture); break; case 7: //pTexture = pMapInfo->pEncounterMonster2Texture; //v44 = "%s B"; sprintf(Source, "%s B", pMapInfo->pEncounterMonster2Texture); break; case 8: //pTexture = pMapInfo->pEncounterMonster3Texture; //v44 = "%s B"; sprintf(Source, "%s B", pMapInfo->pEncounterMonster3Texture); break; case 2: //v9 = pMapInfo->uEncounterMonster3AtLeast; //v16 = rand(); //v17 = pMapInfo->uEncounterMonster3AtMost; //pTexture = pMapInfo->pEncounterMonster3Texture; v12 = rand() % (pMapInfo->uEncounterMonster3AtMost - pMapInfo->uEncounterMonster3AtLeast + 1); //v13 = pMapInfo->Dif_M3; v57 = pMapInfo->Dif_M3; v56 = pMapInfo->uEncounterMonster3AtLeast + v12; strcpy(Source, pMapInfo->pEncounterMonster3Texture); break; case 9: //pTexture = pMapInfo->pEncounterMonster1Texture; //v44 = "%s C"; sprintf(Source, "%s C", pMapInfo->pEncounterMonster1Texture); break; case 10: //pTexture = pMapInfo->pEncounterMonster2Texture; //v44 = "%s C"; sprintf(Source, "%s C", pMapInfo->pEncounterMonster2Texture); break; case 11: //pTexture = pMapInfo->pEncounterMonster3Texture; //v44 = "%s C"; sprintf(Source, "%s C", pMapInfo->pEncounterMonster3Texture); break; default: return; } if (Source[0] == '0') return; v57 += a3; if ( v57 > 4 ) v57 = 4; strcpy(Str2, Source); if ( a4 ) v56 = a4; v18 = v56; if ( (signed int)(v56 + uNumActors) >= 500 ) return; pSector = 0; pPosX = spawn->vPosition.x; a4 = spawn->vPosition.y; a3 = spawn->vPosition.z; if ( uCurrentlyLoadedLevelType == LEVEL_Indoor ) pSector = pIndoor->GetSector(spawn->vPosition.x, spawn->vPosition.y, spawn->vPosition.z); v53 = 0; v52 = (((uCurrentlyLoadedLevelType != LEVEL_Outdoor) - 1) & 0x40) + 64; if ( v18 <= 0 ) return; for (uint i = v53; i < v56; ++i) { pMonster = &pActors[uNumActors]; pActors[uNumActors].Reset(); if ( v57 ) { v23 = rand() % 100; v24 = 3; v25 = (unsigned __int16)word_4E8152[3 * v57]; if ( v23 >= v25 ) { if ( v23 < v25 + (unsigned __int16)word_4E8152[3 * v57 + 1] ) v24 = 2; } else v24 = 1; if ( v24 == 1 ) { pTexture = Source; v44 = "%s A"; } else { if ( v24 == 2 ) { pTexture = Source; v44 = "%s B"; } else { if ( v24 != 3 ) continue; pTexture = Source; v44 = "%s C"; } } sprintf(Str2, v44, pTexture); } v50 = pMonsterList->GetMonsterIDByName(Str2); pTexture = Str2; if ( (signed __int16)v50 == -1 ) { sprintf(Str, "Can't create random monster: '%s'! See MapStats.txt and Monsters.txt!", pTexture); MessageBoxA(nullptr, Str, nullptr, 0); ExitProcess(0); } v27 = &pMonsterList->pMonsters[(signed __int16)v50]; v28 = pMonsterStats->FindMonsterByTextureName(pTexture); if ( !v28 ) v28 = 1; Src = &pMonsterStats->pInfos[v28]; strcpy(pMonster->pActorName, Src->pName); pMonster->sCurrentHP = Src->uHP; assert(sizeof(MonsterInfo) == 88); memcpy(&pMonster->pMonsterInfo, Src, sizeof(MonsterInfo));//Uninitialized portail memory access pMonster->word_000086_some_monster_id = v50 + 1; pMonster->uActorRadius = v27->uMonsterRadius; pMonster->uActorHeight = v27->uMonsterHeight; pMonster->uMovementSpeed = v27->uMovementSpeed; pMonster->vInitialPosition.x = spawn->vPosition.x; pMonster->vPosition.x = spawn->vPosition.x; pMonster->uTetherDistance = 256; pMonster->vInitialPosition.y = a4; pMonster->vPosition.y = a4; pMonster->vInitialPosition.z = a3; pMonster->vPosition.z = a3; pMonster->uSectorID = pSector; pMonster->uGroup = spawn->uGroup; pMonster->PrepareSprites(0); pMonster->pMonsterInfo.uHostilityType = MonsterInfo::Hostility_Friendly; v32 = rand(); a3 = fixpoint_mul(stru_5C6E00->Cos(v32 % 2048), v52); pPosX = a3 + spawn->vPosition.x; a3 = fixpoint_mul(stru_5C6E00->Sin(v32 % 2048), v52); a4 = a3 + spawn->vPosition.y; a3 = spawn->vPosition.z; if ( uCurrentlyLoadedLevelType == LEVEL_Outdoor ) { if ( a5 ) pMonster->uAttributes |= ACTOR_AGGRESSOR; ++uNumActors; continue; } v37 = pIndoor->GetSector(pPosX, a4, spawn->vPosition.z); if ( v37 == pSector ) { v38 = BLV_GetFloorLevel(pPosX, a4, a3, v37, &uFaceID); v39 = v38; if ( v38 != -30000 ) { if ( abs(v38 - a3) <= 1024 ) { a3 = v39; if ( a5 ) pMonster->uAttributes |= ACTOR_AGGRESSOR; ++uNumActors; continue; } } } ; //v53 = (char *)v53 + 1; //result = v53; } //while ( (signed int)v53 < v56 ); } //----- (00438F8F) -------------------------------------------------------- void area_of_effect__damage_evaluate() { int attacker_type; // ecx@3 signed int v3; // eax@3 unsigned int target_id; // edi@6 int target_type; // eax@6 int v10; // edi@8 Vec3_int_ attacker_coord; // ST04_12@9 // int v12; // ST0C_4@10 int v15; // edx@15 int v19; // edi@15 int v23; // edx@18 int v24; // eax@18 // int v30; // eax@29 int v31; // edx@29 int v32; // eax@29 int v33; // ST24_4@29 SpriteObject *v36; // [sp+0h] [bp-28h]@0 int attacker_id; // [sp+10h] [bp-18h]@1 int v44; // [sp+14h] [bp-14h]@15 //Vec3_int_ *pVelocity; // [sp+1Ch] [bp-Ch]@2 signed int a1; // [sp+20h] [bp-8h]@8 int v48; // [sp+24h] [bp-4h]@8 for (attacker_id = 0; attacker_id < AttackerInfo.count; ++attacker_id) { attacker_type = PID_TYPE(AttackerInfo.pIDs[attacker_id]); v3 = PID_ID(AttackerInfo.pIDs[attacker_id]); if (attacker_type == 2) { v36 = &pSpriteObjects[v3]; attacker_type = PID_TYPE(pSpriteObjects[v3].spell_caster_pid); v3 = PID_ID(pSpriteObjects[v3].spell_caster_pid); } if (AttackerInfo.field_3EC[attacker_id] & 1) { target_id = PID_ID(ai_near_actors_targets_pid[v3]); target_type = PID_TYPE(ai_near_actors_targets_pid[v3]) - 3; if (target_type) { if (target_type == 1)//party damage from monsters(повреждения группе от монстров) { v10 = pParty->vPosition.y - AttackerInfo.pYs[attacker_id]; a1 = pParty->vPosition.x - AttackerInfo.pXs[attacker_id]; v48 = pParty->vPosition.y - AttackerInfo.pYs[attacker_id]; if (a1 * a1 + v10 * v10 + ((signed int)(pParty->vPosition.z + pParty->uPartyHeight) >> (1 - AttackerInfo.pZs[attacker_id])) * ((signed int)(pParty->vPosition.z + pParty->uPartyHeight) >> (1 - AttackerInfo.pZs[attacker_id])) < (unsigned int)((AttackerInfo.field_324[attacker_id] + 32) * (AttackerInfo.field_324[attacker_id] + 32))) { attacker_coord.x = AttackerInfo.pXs[attacker_id]; attacker_coord.y = AttackerInfo.pYs[attacker_id]; attacker_coord.z = AttackerInfo.pZs[attacker_id]; if (sub_407A1C(pParty->vPosition.x, pParty->vPosition.y, pParty->vPosition.z + pParty->sEyelevel, attacker_coord)) DamagePlayerFromMonster(AttackerInfo.pIDs[attacker_id], AttackerInfo.field_450[attacker_id], &AttackerInfo.vec_4B4[attacker_id], stru_50C198.which_player_to_attack(&pActors[v3])); } } } else//Actor damage from monsters(повреждение местного жителя) { if (SHIDWORD(pActors[target_id].pActorBuffs[ACTOR_BUFF_PARALYZED].uExpireTime) > 0 || SHIDWORD(pActors[target_id].pActorBuffs[ACTOR_BUFF_PARALYZED].uExpireTime) >= 0 && LODWORD(pActors[target_id].pActorBuffs[ACTOR_BUFF_PARALYZED].uExpireTime) || pActors[target_id].CanAct()) { v15 = pActors[target_id].vPosition.y - AttackerInfo.pYs[attacker_id]; a1 = pActors[target_id].vPosition.x - AttackerInfo.pXs[attacker_id]; v44 = pActors[target_id].vPosition.z; v19 = AttackerInfo.field_324[attacker_id] + pActors[target_id].uActorRadius; v48 = v15; if (a1 * a1 + v15 * v15 + (pActors[target_id].vPosition.z + (pActors[target_id].uActorHeight >> 1) - AttackerInfo.pZs[attacker_id]) * (pActors[target_id].vPosition.z + (pActors[target_id].uActorHeight >> 1) - AttackerInfo.pZs[attacker_id]) < (unsigned int)(v19 * v19)) { attacker_coord.x = AttackerInfo.pXs[attacker_id]; attacker_coord.y = AttackerInfo.pYs[attacker_id]; attacker_coord.z = AttackerInfo.pZs[attacker_id]; if (sub_407A1C(pActors[target_id].vPosition.x, pActors[target_id].vPosition.y, pActors[target_id].vPosition.z + 50, attacker_coord)) { Vec3_int_::Normalize(&a1, &v48, &v44); AttackerInfo.vec_4B4[attacker_id].x = a1; AttackerInfo.vec_4B4[attacker_id].y = v48; AttackerInfo.vec_4B4[attacker_id].z = v44; Actor::ActorDamageFromMonster(AttackerInfo.pIDs[attacker_id], target_id, &AttackerInfo.vec_4B4[attacker_id], AttackerInfo.field_450[attacker_id]); } } } } } else //damage from spells(повреждения от заклов(метеоритный дождь)) { v23 = pParty->vPosition.y - AttackerInfo.pYs[attacker_id]; v24 = ((signed int)pParty->uPartyHeight / 2) - AttackerInfo.pZs[attacker_id]; a1 = pParty->vPosition.x - AttackerInfo.pXs[attacker_id]; v48 = pParty->vPosition.y - AttackerInfo.pYs[attacker_id]; if (a1 * a1 + v23 * v23 + (pParty->vPosition.z + v24) * (pParty->vPosition.z + v24) < (unsigned int)((AttackerInfo.field_324[attacker_id] + 32) * (AttackerInfo.field_324[attacker_id] + 32))) {//party damage (повреждения группе) attacker_coord.x = AttackerInfo.pXs[attacker_id]; attacker_coord.y = AttackerInfo.pYs[attacker_id]; attacker_coord.z = AttackerInfo.pZs[attacker_id]; if (sub_407A1C(pParty->vPosition.x, pParty->vPosition.y, pParty->vPosition.z + pParty->sEyelevel, attacker_coord)) { for (uint i = 0; i < 4; ++i) { if (!(HIDWORD(pParty->pPlayers[i].pConditions[Condition_Dead]) | LODWORD(pParty->pPlayers[i].pConditions[Condition_Dead])) && !pParty->pPlayers[i].pConditions[Condition_Pertified] && !pParty->pPlayers[i].pConditions[Condition_Eradicated]) DamagePlayerFromMonster(AttackerInfo.pIDs[attacker_id], AttackerInfo.field_450[attacker_id], &AttackerInfo.vec_4B4[attacker_id], i); } } } if ((signed int)uNumActors > 0) {//actors damage(повреждения другим участникам) for (int actorID = 0; (signed int)actorID < (signed int)uNumActors; ++actorID) { if (pActors[actorID].CanAct()) { //v30 = pActors[actorID].vPosition.y - AttackerInfo.pYs[attacker_id]; a1 = pActors[actorID].vPosition.x - AttackerInfo.pXs[attacker_id]; v31 = pActors[actorID].vPosition.z; v48 = pActors[actorID].vPosition.y - AttackerInfo.pYs[attacker_id]; v44 = pActors[actorID].vPosition.z; v32 = (pActors[actorID].uActorHeight / 2) - AttackerInfo.pZs[attacker_id]; v33 = pActors[actorID].uActorRadius + AttackerInfo.field_324[attacker_id]; if (a1 * a1 + v48 * v48 + (v31 + v32) * (v31 + v32) < (unsigned int)(v33 * v33)) { attacker_coord.x = AttackerInfo.pXs[attacker_id]; attacker_coord.y = AttackerInfo.pYs[attacker_id]; attacker_coord.z = AttackerInfo.pZs[attacker_id]; if (sub_407A1C(pActors[actorID].vPosition.x, pActors[actorID].vPosition.y, pActors[actorID].vPosition.z + 50, attacker_coord))//что делает ф-ция? { Vec3_int_::Normalize(&a1, &v48, &v44); AttackerInfo.vec_4B4[attacker_id].x = a1; AttackerInfo.vec_4B4[attacker_id].y = v48; AttackerInfo.vec_4B4[attacker_id].z = v44; switch (attacker_type) { case OBJECT_Player: Actor::DamageMonsterFromParty(AttackerInfo.pIDs[attacker_id], actorID, &AttackerInfo.vec_4B4[attacker_id]); break; case OBJECT_Actor: if (v36 && pActors[v3].GetActorsRelation(&pActors[actorID])) Actor::ActorDamageFromMonster(AttackerInfo.pIDs[attacker_id], actorID, &AttackerInfo.vec_4B4[attacker_id], v36->field_61); break; case OBJECT_Item: ItemDamageFromActor(AttackerInfo.pIDs[attacker_id], actorID, &AttackerInfo.vec_4B4[attacker_id]); break; } } } } } } } } AttackerInfo.count = 0; } //----- (0043AE12) -------------------------------------------------------- double __fastcall sub_43AE12(signed int a1) { //signed int v1; // ST00_4@1 signed int v2; // ecx@1 double v3; // st7@1 double result; // st7@6 v3 = (double)a1; for (v2 = 0; v2 < 5; ++v2) { if (v3 < flt_4E4A80[v2 + 5]) break; } if (v2 <= 0 || v2 >= 5) { if (v2) result = flt_4E4A80[4]; else result = flt_4E4A80[0]; } else result = (flt_4E4A80[v2] - flt_4E4A80[v2 - 1]) * (v3 - flt_4E4A80[v2 + 4]) / (flt_4E4A80[v2 + 5] - flt_4E4A80[v2 + 4]) + flt_4E4A80[v2]; return result; } //----- (0043B057) -------------------------------------------------------- void ItemDamageFromActor(unsigned int uObjID, unsigned int uActorID, Vec3_int_ *pVelocity) { int v6; // eax@4 int damage; // edi@4 int a2a; // [sp+Ch] [bp-4h]@8 if (!pActors[uActorID].IsNotAlive()) { if (PID_TYPE(uObjID) == OBJECT_Item) { if (pSpriteObjects[PID_ID(uObjID)].spell_id) { v6 = _43AFE3_calc_spell_damage(pSpriteObjects[PID_ID(uObjID)].spell_id, pSpriteObjects[PID_ID(uObjID)].spell_level, pSpriteObjects[PID_ID(uObjID)].spell_skill, pActors[uActorID].sCurrentHP); damage = pActors[uActorID].CalcMagicalDamageToActor((DAMAGE_TYPE)0, v6); pActors[uActorID].sCurrentHP -= damage; if (damage) { if (pActors[uActorID].sCurrentHP > 0) Actor::AI_Stun(uActorID, uObjID, 0); else Actor::Die(uActorID); a2a = 20 * damage / (signed int)pActors[uActorID].pMonsterInfo.uHP; if (20 * damage / (signed int)pActors[uActorID].pMonsterInfo.uHP > 10) a2a = 10; if (!MonsterStats::BelongsToSupertype(pActors[uActorID].pMonsterInfo.uID, MONSTER_SUPERTYPE_TREANT)) { pVelocity->x = fixpoint_mul(a2a, pVelocity->x); pVelocity->y = fixpoint_mul(a2a, pVelocity->y); pVelocity->z = fixpoint_mul(a2a, pVelocity->z); pActors[uActorID].vVelocity.x = 50 * LOWORD(pVelocity->x); pActors[uActorID].vVelocity.y = 50 * LOWORD(pVelocity->y); pActors[uActorID].vVelocity.z = 50 * LOWORD(pVelocity->z); } Actor::AddBloodsplatOnDamageOverlay(uActorID, 1, damage); } else Actor::AI_Stun(uActorID, uObjID, 0); } } } }