diff options
Diffstat (limited to '')
49 files changed, 8804 insertions, 252 deletions
diff --git a/src/animation/AnimBlendAssociation.cpp b/src/animation/AnimBlendAssociation.cpp index d94fe2c1..7273c846 100644 --- a/src/animation/AnimBlendAssociation.cpp +++ b/src/animation/AnimBlendAssociation.cpp @@ -185,7 +185,7 @@ CAnimBlendAssociation::UpdateBlend(float timeDelta) if(blendAmount <= 0.0f && blendDelta < 0.0f){ // We're faded out and are not fading in blendAmount = 0.0f; - blendDelta = max(0.0, blendDelta); + blendDelta = Max(0.0, blendDelta); if(flags & ASSOC_DELETEFADEDOUT){ if(callbackType == CB_FINISH || callbackType == CB_DELETE) callback(this, callbackArg); @@ -197,7 +197,7 @@ CAnimBlendAssociation::UpdateBlend(float timeDelta) if(blendAmount > 1.0f){ // Maximally faded in, clamp values blendAmount = 1.0f; - blendDelta = min(0.0, blendDelta); + blendDelta = Min(0.0, blendDelta); } return true; diff --git a/src/animation/AnimBlendHierarchy.cpp b/src/animation/AnimBlendHierarchy.cpp index e4bcdc69..e594e5d8 100644 --- a/src/animation/AnimBlendHierarchy.cpp +++ b/src/animation/AnimBlendHierarchy.cpp @@ -36,7 +36,7 @@ CAnimBlendHierarchy::CalcTotalTime(void) float seqTime = 0.0f; for(j = 0; j < sequences[i].numFrames; j++) seqTime += sequences[i].GetKeyFrame(j)->deltaTime; - totalTime = max(totalTime, seqTime); + totalTime = Max(totalTime, seqTime); } totalLength = totalTime; } diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp index e586366d..76bfb793 100644 --- a/src/audio/AudioManager.cpp +++ b/src/audio/AudioManager.cpp @@ -6,6 +6,7 @@ #include "Automobile.h" #include "Camera.h" #include "DMAudio.h" +#include "Explosion.h" #include "Garages.h" #include "ModelIndices.h" #include "MusicManager.h" @@ -16,6 +17,7 @@ #include "Pools.h" #include "sampman.h" #include "Stats.h" +#include "SurfaceTable.h" #include "Vehicle.h" #include "Plane.h" #include "World.h" @@ -96,10 +98,54 @@ enum eVehicleModel { CAR159, }; -void *cAudioScriptObject::operator new(size_t sz) { return CPools::GetAudioScriptObjectPool()->New(); } -void *cAudioScriptObject::operator new(size_t sz, int handle) { return CPools::GetAudioScriptObjectPool()->New(handle); } -void cAudioScriptObject::operator delete(void *p, size_t sz) { CPools::GetAudioScriptObjectPool()->Delete((cAudioScriptObject*)p); } -void cAudioScriptObject::operator delete(void *p, int handle) { CPools::GetAudioScriptObjectPool()->Delete((cAudioScriptObject*)p); } +void +cPedComments::Add(tPedComment *com) +{ + uint8 index; + + if(nrOfCommentsInBank[activeBank] >= 20u) { + index = field_1120[activeBank][19]; + if(m_asPedComments[activeBank][index].m_bVolume > com->m_bVolume) return; + } else { + index = nrOfCommentsInBank[activeBank]++; + } + m_asPedComments[activeBank][index] = *com; + + uint32 i = 0; + if(index != 0) { + for(i = 0; i < index; i++) { + if(m_asPedComments[activeBank][field_1120[activeBank][i]].m_bVolume < + m_asPedComments[activeBank][index].m_bVolume) { + memmove(&field_1120[activeBank][i + 1], &field_1120[activeBank][i], + 19 - i); + break; + } + } + } + + field_1120[activeBank][i] = index; +} + +void * +cAudioScriptObject::operator new(size_t sz) +{ + return CPools::GetAudioScriptObjectPool()->New(); +} +void * +cAudioScriptObject::operator new(size_t sz, int handle) +{ + return CPools::GetAudioScriptObjectPool()->New(handle); +} +void +cAudioScriptObject::operator delete(void *p, size_t sz) +{ + CPools::GetAudioScriptObjectPool()->Delete((cAudioScriptObject *)p); +} +void +cAudioScriptObject::operator delete(void *p, int handle) +{ + CPools::GetAudioScriptObjectPool()->Delete((cAudioScriptObject *)p); +} cAudioManager &AudioManager = *(cAudioManager *)0x880FC0; @@ -407,9 +453,9 @@ cAudioManager::ComputeDopplerEffectedFrequency(uint32 oldFreq, float position1, float speedOfSource = (dist / field_19195) * speedMultiplier; if(speedOfSound > fabsf(speedOfSource)) { if(speedOfSource < 0.0f) { - speedOfSource = max(speedOfSource, -1.5f); + speedOfSource = Max(speedOfSource, -1.5f); } else { - speedOfSource = min(speedOfSource, 1.5f); + speedOfSource = Min(speedOfSource, 1.5f); } newFreq = (oldFreq * speedOfSound) / (speedOfSource + speedOfSound); } @@ -830,7 +876,7 @@ cAudioManager::SetupJumboFlySound(uint8 emittingVol) SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSoundIntensity = 440.0f; m_sQueueSample.field_56 = 0; - m_sQueueSample.field_48 = 4.0; + m_sQueueSample.field_48 = 4.0f; m_sQueueSample.m_bReverbFlag = 1; m_sQueueSample.field_76 = 5; m_sQueueSample.m_nLoopEnd = @@ -861,8 +907,8 @@ cAudioManager::SetupJumboRumbleSound(uint8 emittingVol) SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); - m_sQueueSample.field_48 = 4.0; - m_sQueueSample.m_fSoundIntensity = 240.0; + m_sQueueSample.field_48 = 4.0f; + m_sQueueSample.m_fSoundIntensity = 240.0f; m_sQueueSample.field_56 = 0; m_sQueueSample.field_76 = 12; m_sQueueSample.m_bOffset = 0; @@ -3729,7 +3775,6 @@ cAudioManager::ProcessAirBrakes(cVehicleParams *params) void cAudioManager::ProcessAirportScriptObject(uint8 sound) { - float dist; float distSquared; float maxDist; @@ -4002,68 +4047,68 @@ cAudioManager::ProcessEntity(int32 id) case AUDIOTYPE_PHYSICAL: if(!m_bUserPause) { m_sQueueSample.m_bReverbFlag = 1; - cAudioManager::ProcessPhysical(id); + ProcessPhysical(id); } break; case AUDIOTYPE_EXPLOSION: if(!m_bUserPause) { m_sQueueSample.m_bReverbFlag = 1; - cAudioManager::ProcessExplosions(id); + ProcessExplosions(id); } break; case AUDIOTYPE_FIRE: if(!m_bUserPause) { m_sQueueSample.m_bReverbFlag = 1; - cAudioManager::ProcessFires(id); + ProcessFires(id); } break; case AUDIOTYPE_WEATHER: if(!m_bUserPause) { m_sQueueSample.m_bReverbFlag = 1; - cAudioManager::ProcessWeather(id); + ProcessWeather(id); } break; case AUDIOTYPE_CRANE: if(!m_bUserPause) { m_sQueueSample.m_bReverbFlag = 1; - cAudioManager::ProcessCrane(); + ProcessCrane(); } break; case AUDIOTYPE_SCRIPTOBJECT: if(!m_bUserPause) { m_sQueueSample.m_bReverbFlag = 1; - cAudioManager::ProcessScriptObject(id); + ProcessScriptObject(id); } break; case AUDIOTYPE_BRIDGE: if(!m_bUserPause) { m_sQueueSample.m_bReverbFlag = 1; - cAudioManager::ProcessBridge(); + ProcessBridge(); } break; case AUDIOTYPE_FRONTEND: m_sQueueSample.m_bReverbFlag = 0; - cAudioManager::ProcessFrontEnd(); + ProcessFrontEnd(); break; case AUDIOTYPE_PROJECTILE: if(!m_bUserPause) { m_sQueueSample.m_bReverbFlag = 1; - cAudioManager::ProcessProjectiles(); + ProcessProjectiles(); } break; case AUDIOTYPE_GARAGE: - if(!m_bUserPause) cAudioManager::ProcessGarages(); + if(!m_bUserPause) ProcessGarages(); break; case AUDIOTYPE_FIREHYDRANT: if(!m_bUserPause) { m_sQueueSample.m_bReverbFlag = 1; - cAudioManager::ProcessFireHydrant(); + ProcessFireHydrant(); } break; case AUDIOTYPE_WATERCANNON: if(!m_bUserPause) { m_sQueueSample.m_bReverbFlag = 1; - cAudioManager::ProcessWaterCannon(id); + ProcessWaterCannon(id); } break; default: return; @@ -4071,11 +4116,88 @@ cAudioManager::ProcessEntity(int32 id) } } -WRAPPER void cAudioManager::ProcessExplosions(int32 explosion) { - EAXJMP(0x575AC0); + uint8 type; + CVector *pos; + float maxDist; + float distSquared; + + for(uint8 i = 0; i < 48u; i++) { + if(CExplosion::GetExplosionActiveCounter(i) == 1) { + CExplosion::ResetExplosionActiveCounter(i); + type = CExplosion::GetExplosionType(i); + switch(type) { + case EXPLOSION_GRENADE: + case EXPLOSION_ROCKET: + case EXPLOSION_BARREL: + case EXPLOSION_TANK_GRENADE: + maxDist = 160000.f; + m_sQueueSample.m_fSoundIntensity = 400.0f; + m_sQueueSample.m_nSampleIndex = + AUDIO_SAMPLE_WEAPON_GRENADE_EXPLOSION; + m_sQueueSample.m_nFrequency = RandomDisplacement(2000u) + 38000; + m_sQueueSample.field_16 = 0; + m_sQueueSample.m_bBankIndex = 0; + break; + case EXPLOSION_MOLOTOV: + maxDist = 40000.f; + m_sQueueSample.m_fSoundIntensity = 200.0f; + m_sQueueSample.m_nSampleIndex = + AUDIO_SAMPLE_WEAPON_MOLOTOV_EXPLOSION; + m_sQueueSample.m_nFrequency = RandomDisplacement(1000u) + 19000; + m_sQueueSample.field_16 = 0; + m_sQueueSample.m_bBankIndex = 0; + break; + case EXPLOSION_MINE: + case EXPLOSION_HELI_BOMB: + maxDist = 90000.f; + m_sQueueSample.m_fSoundIntensity = 300.0f; + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_WEAPON_ROCKET_SHOT; + m_sQueueSample.m_nFrequency = RandomDisplacement(1000u) + 12347; + m_sQueueSample.field_16 = 0; + m_sQueueSample.m_bBankIndex = 0; + break; + default: + maxDist = 160000.f; + m_sQueueSample.m_fSoundIntensity = 400.0f; + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_EXPLOSION_CAR; + m_sQueueSample.m_nFrequency = RandomDisplacement(2000u) + 38000; + if(type == EXPLOSION_HELI) + m_sQueueSample.m_nFrequency = + 8 * m_sQueueSample.m_nFrequency / 10u; + m_sQueueSample.field_16 = 0; + m_sQueueSample.m_bBankIndex = 0; + break; + } + pos = CExplosion::GetExplosionPosition(i); + m_sQueueSample.m_vecPos.x = pos->x; + m_sQueueSample.m_vecPos.y = pos->y; + m_sQueueSample.m_vecPos.z = pos->z; + distSquared = GetDistanceSquared(&m_sQueueSample.m_vecPos); + if(distSquared < maxDist) { + m_sQueueSample.m_fDistance = Sqrt(distSquared); + m_sQueueSample.m_bVolume = + ComputeVolume(maxVolume, m_sQueueSample.m_fSoundIntensity, + m_sQueueSample.m_fDistance); + if(m_sQueueSample.m_bVolume) { + m_sQueueSample.field_4 = i; + m_sQueueSample.field_48 = 2.0f; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.field_56 = 1; + m_sQueueSample.m_bReverbFlag = 1; + m_sQueueSample.m_bEmittingVolume = maxVolume; + m_sQueueSample.m_nLoopStart = 0; + m_sQueueSample.m_nLoopEnd = -1; + m_sQueueSample.m_bReverbFlag = 1; + m_sQueueSample.m_bRequireReflection = 1; + AddSampleToRequestedQueue(); + } + } + } + } } void @@ -5220,11 +5342,1001 @@ cAudioManager::ProcessLoopingScriptObject(uint8 sound) } } -WRAPPER void -cAudioManager::ProcessPed(CPhysical *) +cAudioManager::ProcessOneShotScriptObject(uint8 sound) +{ + CPlayerPed *playerPed; + uint8 emittingVolume; + float maxDist; + float distSquared; + + cPedParams male; + cPedParams female; + + static uint8 iSound = 0; + + switch(sound) { + case SCRIPT_SOUND_INJURED_PED_MALE_OUCH_S: + case SCRIPT_SOUND_INJURED_PED_MALE_OUCH_L: + male.m_pPed = nil; + male.m_bDistanceCalculated = 0; + male.m_fDistance = GetDistanceSquared(&m_sQueueSample.m_vecPos); + cAudioManager::SetupPedComments(&male, SOUND_INJURED_PED_MALE_OUCH); + return; + case SCRIPT_SOUND_INJURED_PED_FEMALE_OUCH_S: + case SCRIPT_SOUND_INJURED_PED_FEMALE_OUCH_L: + female.m_pPed = nil; + female.m_bDistanceCalculated = 0; + female.m_fDistance = GetDistanceSquared(&m_sQueueSample.m_vecPos); + cAudioManager::SetupPedComments(&female, SOUND_INJURED_PED_FEMALE); + return; + case SCRIPT_SOUND_GATE_START_CLUNK: + case SCRIPT_SOUND_GATE_STOP_CLUNK: + maxDist = 1600.f; + m_sQueueSample.m_fSoundIntensity = 40.0f; + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_COLLISION_GATE; + m_sQueueSample.m_bBankIndex = 0; + if(sound == SCRIPT_SOUND_GATE_START_CLUNK) + m_sQueueSample.m_nFrequency = 10600; + else + m_sQueueSample.m_nFrequency = 9000; + m_sQueueSample.field_16 = 1; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.m_bRequireReflection = 1; + emittingVolume = RandomDisplacement(10u) + 50; + break; + case SCRIPT_SOUND_BULLET_HIT_GROUND_1: + case SCRIPT_SOUND_BULLET_HIT_GROUND_2: + case SCRIPT_SOUND_BULLET_HIT_GROUND_3: + maxDist = 2500.f; + m_sQueueSample.m_fSoundIntensity = 50.0f; + m_sQueueSample.m_nSampleIndex = + m_anRandomTable[iSound % 5] % 3u + AUDIO_SAMPLE_BULLET_HIT_GROUND_1; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.m_nFrequency = + cSampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.field_16 = 9; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_bIsDistant = 0; + emittingVolume = m_anRandomTable[2] % 20 + 90; + break; + case SCRIPT_SOUND_110: + case SCRIPT_SOUND_111: + if(cSampleManager.IsSampleBankLoaded(0) != 1) return; + maxDist = 6400.f; + m_sQueueSample.m_fSoundIntensity = 80.0f; + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_389; + m_sQueueSample.m_bBankIndex = 0; + emittingVolume = 127; + m_sQueueSample.m_nFrequency = + cSampleManager.GetSampleBaseFrequency(AUDIO_SAMPLE_389); + m_sQueueSample.field_16 = 0; + m_sQueueSample.field_48 = 2.0f; + m_sQueueSample.m_bIsDistant = 0; + break; + case SCRIPT_SOUND_PAYPHONE_RINGING: + maxDist = 6400.f; + m_sQueueSample.m_fSoundIntensity = 80.0f; + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_PAYPHONE_RINGING; + m_sQueueSample.m_bBankIndex = 0; + emittingVolume = 80; + m_sQueueSample.m_nFrequency = + cSampleManager.GetSampleBaseFrequency(AUDIO_SAMPLE_PAYPHONE_RINGING); + m_sQueueSample.field_16 = 1; + m_sQueueSample.field_48 = 2.0f; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.m_bRequireReflection = 0; + break; + case SCRIPT_SOUND_GLASS_BREAK_L: + maxDist = 3600.f; + m_sQueueSample.m_fSoundIntensity = 60.0f; + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_GLASS_GENERIC_BREAK; + m_sQueueSample.m_bBankIndex = 0; + emittingVolume = 70; + m_sQueueSample.m_nFrequency = + cSampleManager.GetSampleBaseFrequency(AUDIO_SAMPLE_GLASS_GENERIC_BREAK); + m_sQueueSample.field_16 = 3; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_bIsDistant = 0; + break; + case SCRIPT_SOUND_GLASS_BREAK_S: + maxDist = 3600.f; + m_sQueueSample.m_fSoundIntensity = 60.0f; + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_GLASS_GENERIC_BREAK; + m_sQueueSample.m_bBankIndex = 0; + emittingVolume = 60; + m_sQueueSample.m_nFrequency = + cSampleManager.GetSampleBaseFrequency(AUDIO_SAMPLE_GLASS_GENERIC_BREAK); + m_sQueueSample.field_16 = 3; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_bIsDistant = 0; + break; + case SCRIPT_SOUND_GLASS_CRACK: + maxDist = 3600.f; + m_sQueueSample.m_fSoundIntensity = 60.0f; + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_GLASS_WINDSHIELD_CRACK; + m_sQueueSample.m_bBankIndex = 0; + emittingVolume = 70; + m_sQueueSample.m_nFrequency = + cSampleManager.GetSampleBaseFrequency(AUDIO_SAMPLE_GLASS_WINDSHIELD_CRACK); + m_sQueueSample.field_16 = 3; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.m_bRequireReflection = 1; + break; + case SCRIPT_SOUND_GLASS_LIGHT_BREAK: + maxDist = 3025.f; + m_sQueueSample.m_fSoundIntensity = 55.0f; + m_sQueueSample.m_nSampleIndex = + (m_anRandomTable[4] & 3) + AUDIO_SAMPLE_GLASS_LIGHT_BREAK_1; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.m_nFrequency = RandomDisplacement(2000u) + 19000; + m_sQueueSample.field_16 = 9; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_bIsDistant = 0; + emittingVolume = RandomDisplacement(11u) + 25; + break; + case SCRIPT_SOUND_BOX_DESTROYED_1: + maxDist = 3600.f; + m_sQueueSample.m_fSoundIntensity = 60.0f; + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_BOX_DESTROYED_1; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.m_nFrequency = RandomDisplacement(1500u) + 18600; + m_sQueueSample.field_16 = 3; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.m_bRequireReflection = 1; + emittingVolume = m_anRandomTable[2] % 20 + 80; + break; + case SCRIPT_SOUND_BOX_DESTROYED_2: + maxDist = 3600.f; + m_sQueueSample.m_fSoundIntensity = 60.0f; + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_BOX_DESTROYED_2; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.m_nFrequency = RandomDisplacement(1500u) + 18600; + m_sQueueSample.field_16 = 3; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.m_bRequireReflection = 1; + emittingVolume = m_anRandomTable[2] % 20 + 80; + break; + case SCRIPT_SOUND_METAL_COLLISION: + maxDist = 3600.f; + m_sQueueSample.m_fSoundIntensity = 60.0f; + m_sQueueSample.m_nSampleIndex = + m_anRandomTable[3] % 5u + AUDIO_SAMPLE_COLLISION_METAL; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.m_nFrequency = + cSampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 4); + m_sQueueSample.field_16 = 3; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.m_bRequireReflection = 1; + emittingVolume = m_anRandomTable[2] % 30 + 70; + break; + case SCRIPT_SOUND_TIRE_COLLISION: + maxDist = 3600.f; + m_sQueueSample.m_fSoundIntensity = 60.0f; + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_COLLISION_RUBBER; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.m_nFrequency = + cSampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 4); + m_sQueueSample.field_16 = 3; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.m_bRequireReflection = 1; + emittingVolume = m_anRandomTable[2] % 30 + 60; + break; + case SCRIPT_SOUND_GUNSHELL_DROP: + playerPed = FindPlayerPed(); + if(playerPed) { + switch(playerPed->m_nSurfaceTouched) { + case SURFACE_GRASS: + case SURFACE_DIRT: + case SURFACE_DIRTTRACK: + case SURFACE_SCAFFOLD: + case SURFACE_FLESH: + case SURFACE_SAND: + case SURFACE_TIRE: + case SURFACE_HEDGE: + m_sQueueSample.m_nSampleIndex = + AUDIO_SAMPLE_BULLET_GUNSHELL_SOFT_DROP; + m_sQueueSample.m_nFrequency = RandomDisplacement(500u) + 11000; + m_sQueueSample.field_16 = 18; + maxDist = 400.f; + m_sQueueSample.m_fSoundIntensity = 20.0f; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_bIsDistant = 0; + emittingVolume = m_anRandomTable[2] % 20 + 30; + distSquared = GetDistanceSquared(&m_sQueueSample.m_vecPos); + if(distSquared < maxDist) { + m_sQueueSample.m_fDistance = Sqrt(distSquared); + m_sQueueSample.m_bVolume = ComputeVolume( + emittingVolume, m_sQueueSample.m_fSoundIntensity, + m_sQueueSample.m_fDistance); + if(m_sQueueSample.m_bVolume) { + m_sQueueSample.field_4 = iSound++; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.field_56 = 1; + m_sQueueSample.m_bEmittingVolume = emittingVolume; + m_sQueueSample.m_nLoopStart = 0; + m_sQueueSample.m_nLoopEnd = -1; + m_sQueueSample.m_bReverbFlag = 1; + AddSampleToRequestedQueue(); + } + } + return; + case SURFACE_PUDDLE: return; + default: break; + } + } + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_BULLET_GUNSHELL_HARD_DROP; + m_sQueueSample.m_nFrequency = RandomDisplacement(750u) + 18000; + m_sQueueSample.field_16 = 15; + maxDist = 400.f; + m_sQueueSample.m_fSoundIntensity = 20.0f; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_bIsDistant = 0; + emittingVolume = m_anRandomTable[2] % 20 + 30; + break; + case SCRIPT_SOUND_GUNSHELL_DROP_SOFT: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_BULLET_GUNSHELL_SOFT_DROP; + m_sQueueSample.m_nFrequency = RandomDisplacement(500u) + 11000; + m_sQueueSample.field_16 = 18; + maxDist = 400.f; + m_sQueueSample.m_fSoundIntensity = 20.0f; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_bIsDistant = 0; + emittingVolume = m_anRandomTable[2] % 20 + 30; + break; + default: return; + } + + distSquared = GetDistanceSquared(&m_sQueueSample.m_vecPos); + if(distSquared < maxDist) { + m_sQueueSample.m_fDistance = Sqrt(distSquared); + m_sQueueSample.m_bVolume = ComputeVolume( + emittingVolume, m_sQueueSample.m_fSoundIntensity, m_sQueueSample.m_fDistance); + if(m_sQueueSample.m_bVolume) { + m_sQueueSample.field_4 = iSound++; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.field_56 = 1; + m_sQueueSample.m_bEmittingVolume = emittingVolume; + m_sQueueSample.m_nLoopStart = 0; + m_sQueueSample.m_nLoopEnd = -1; + m_sQueueSample.m_bReverbFlag = 1; + AddSampleToRequestedQueue(); + } + } +} + +void +cAudioManager::ProcessPed(CPhysical *ped) { - EAXJMP(0x56F450); + cPedParams params; + + params.m_pPed = 0; + params.m_bDistanceCalculated = 0; + params.m_fDistance = 0.0f; + + m_sQueueSample.m_vecPos = ped->GetPosition(); + + params.m_bDistanceCalculated = 0; + params.m_pPed = (CPed *)ped; + params.m_fDistance = GetDistanceSquared(&m_sQueueSample.m_vecPos); + if(ped->m_modelIndex == MI_FATMALE02) ProcessPedHeadphones(¶ms); + ProcessPedOneShots(¶ms); +} + +void +cAudioManager::ProcessPedHeadphones(cPedParams *params) +{ + CPed *ped; + CVehicle *veh; + uint8 emittingVol; + + if(params->m_fDistance < 49.f) { + ped = params->m_pPed; + if(!(ped->m_ped_flagA20) || ped->m_bodyPartBleeding != 2) { + CalculateDistance((bool *)params, params->m_fDistance); + if(ped->bInVehicle && ped->m_nPedState == PED_DRIVING) { + emittingVol = 10; + veh = ped->m_pMyVehicle; + if(veh && veh->m_type == 0) { + for(int32 i = 2; i < 6; i++) { + if(!veh->IsDoorClosed((eDoors)i) || + veh->IsDoorMissing((eDoors)i)) { + emittingVol = 42; + break; + } + } + } + } else { + emittingVol = 42; + } + + m_sQueueSample.m_bVolume = + ComputeVolume(emittingVol, 7.f, m_sQueueSample.m_fDistance); + if(m_sQueueSample.m_bVolume) { + m_sQueueSample.field_4 = 64; + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_PED_HEADPHONES; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.field_16 = 5; + m_sQueueSample.m_nFrequency = cSampleManager.GetSampleBaseFrequency( + AUDIO_SAMPLE_PED_HEADPHONES); + m_sQueueSample.m_nLoopCount = 0; + m_sQueueSample.m_bEmittingVolume = emittingVol; + m_sQueueSample.m_nLoopStart = + cSampleManager.GetSampleLoopStartOffset( + m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nLoopEnd = cSampleManager.GetSampleLoopEndOffset( + m_sQueueSample.m_nSampleIndex); + m_sQueueSample.field_48 = 4.0f; + m_sQueueSample.m_fSoundIntensity = 7.0f; + m_sQueueSample.field_56 = 0; + m_sQueueSample.field_76 = 5; + m_sQueueSample.m_bReverbFlag = 1; + m_sQueueSample.m_bRequireReflection = 0; + AddSampleToRequestedQueue(); + } + return; + } + } +} + +void +cAudioManager::ProcessPedOneShots(cPedParams *params) +{ + uint8 emittingVol; + int32 sampleIndex; + + CPed *ped = params->m_pPed; + + char processed; + int16 sound; + char noReflection; + CWeapon *weapon; + uint16 i; + float maxDist; + + static uint8 iSound = 21; + + i = 0; + weapon = nil; + while(i < m_asAudioEntities[m_sQueueSample.m_nEntityIndex].field_24) { + noReflection = 0; + processed = 0; + m_sQueueSample.m_bRequireReflection = 0; + sound = m_asAudioEntities[0].m_awAudioEvent[i + 20 * m_sQueueSample.m_nEntityIndex]; + switch(sound) { + case SOUND_STEP_START: + case SOUND_STEP_END: + if(!params->m_pPed->bIsLooking) { + emittingVol = m_anRandomTable[3] % 15u + 45; + if(FindPlayerPed() != + m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_pEntity) + emittingVol >>= 1; + maxDist = 400.f; + switch(params->m_pPed->m_nSurfaceTouched) { + case SURFACE_GRASS: + sampleIndex = + m_anRandomTable[1] % 5u + AUDIO_SAMPLE_PED_FALL_GRASS_1; + break; + case SURFACE_DIRT: + case SURFACE_DIRTTRACK: + sampleIndex = + m_anRandomTable[4] % 5u + AUDIO_SAMPLE_PED_FALL_DIRT_1; + break; + case SURFACE_METAL6: + case SURFACE_METAL_DOOR: + case SURFACE_BILLBOARD: + case SURFACE_STEEL: + case SURFACE_METAL_POLE: + case SURFACE_STREET_LIGHT: + case SURFACE_METAL14: + case SURFACE_METAL15: + case SURFACE_METAL_FENCE: + case SURFACE_METAL27: + case SURFACE_METAL28: + sampleIndex = + m_anRandomTable[0] % 5u + AUDIO_SAMPLE_PED_FALL_METAL_1; + break; + case SURFACE_SAND: + sampleIndex = + (m_anRandomTable[4] & 3) + AUDIO_SAMPLE_PED_FALL_SAND_1; + break; + case SURFACE_PUDDLE: + sampleIndex = (m_anRandomTable[3] & 3) + + AUDIO_SAMPLE_PED_FALL_IN_WATER_1; + break; + case SURFACE_WOOD: + case SURFACE_WOOD_BOX: + case SURFACE_WOOD_PLANK: + sampleIndex = + m_anRandomTable[2] % 5u + AUDIO_SAMPLE_PED_FALL_WOOD_1; + break; + case SURFACE_HEDGE: + sampleIndex = + m_anRandomTable[2] % 5u + AUDIO_SAMPLE_COLLISION_HEDGE; + break; + default: + sampleIndex = m_anRandomTable[2] % 5u + + AUDIO_SAMPLE_PED_FALL_PAVEMENT_1; + break; + } + m_sQueueSample.m_nSampleIndex = sampleIndex; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_4 = + m_asAudioEntities[0] + .m_awAudioEvent[i + 20 * m_sQueueSample.m_nEntityIndex] - + 28; + m_sQueueSample.m_nFrequency = cSampleManager.GetSampleBaseFrequency( + m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += + RandomDisplacement(m_sQueueSample.m_nFrequency / 17u); + switch(params->m_pPed->m_nMoveState) { + case 2: + emittingVol >>= 2; + m_sQueueSample.m_nFrequency = + 9 * m_sQueueSample.m_nFrequency / 10u; + break; + case 3: + emittingVol >>= 1; + m_sQueueSample.m_nFrequency = + 11 * m_sQueueSample.m_nFrequency / 10u; + break; + case 4: + m_sQueueSample.m_nFrequency = + 12 * m_sQueueSample.m_nFrequency / 10u; + break; + default: break; + } + m_sQueueSample.field_16 = 5; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_fSoundIntensity = 20.0f; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_nLoopStart = 0; + m_sQueueSample.m_nLoopEnd = -1; + m_sQueueSample.m_bEmittingVolume = emittingVol; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.field_56 = 1; + m_sQueueSample.m_bRequireReflection = 1; + goto LABEL_84; + } + break; + case SOUND_FALL_LAND: + case SOUND_FALL_COLLAPSE: + if(!ped->bIsLooking) { + maxDist = 900.f; + emittingVol = m_anRandomTable[3] % 20 + 80; + if(ped->m_nSurfaceTouched == SURFACE_PUDDLE) { + m_sQueueSample.m_nSampleIndex = + (m_anRandomTable[3] & 3) + + AUDIO_SAMPLE_PED_FALL_IN_WATER_1; + } else if(sound == SOUND_FALL_LAND) { + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_PED_FALL_LAND; + } else { + m_sQueueSample.m_nSampleIndex = + AUDIO_SAMPLE_PED_FALL_COLLAPSE; + } + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_4 = 1; + m_sQueueSample.m_nFrequency = cSampleManager.GetSampleBaseFrequency( + m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += + RandomDisplacement(m_sQueueSample.m_nFrequency / 17u); + m_sQueueSample.field_16 = 2; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_fSoundIntensity = 30.0f; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_nLoopStart = 0; + m_sQueueSample.m_nLoopEnd = -1; + m_sQueueSample.m_bEmittingVolume = emittingVol; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.field_56 = 1; + m_sQueueSample.m_bRequireReflection = 1; + goto LABEL_84; + } + break; + case SOUND_21: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_PED_HIT_1; + m_sQueueSample.m_nFrequency = 18000; + goto LABEL_81; + case SOUND_22: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_PED_HIT_1; + m_sQueueSample.m_nFrequency = 16500; + goto LABEL_81; + case SOUND_23: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_PED_HIT_1; + goto LABEL_80; + case SOUND_24: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_PED_HIT_2; + m_sQueueSample.m_nFrequency = 18000; + goto LABEL_81; + case SOUND_25: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_PED_HIT_2; + m_sQueueSample.m_nFrequency = 16500; + goto LABEL_81; + case SOUND_26: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_PED_HIT_2; + goto LABEL_80; + case SOUND_WEAPON_PUNCH_ATTACK: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_PED_HIT_3; + m_sQueueSample.m_nFrequency = 18000; + goto LABEL_81; + case SOUND_28: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_PED_HIT_3; + m_sQueueSample.m_nFrequency = 16500; + goto LABEL_81; + case SOUND_29: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_PED_HIT_3; + goto LABEL_80; + case SOUND_2A: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_PED_HIT_4; + m_sQueueSample.m_nFrequency = 18000; + goto LABEL_81; + case SOUND_2B: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_PED_HIT_4; + m_sQueueSample.m_nFrequency = 16500; + goto LABEL_81; + case SOUND_2C: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_PED_HIT_4; + LABEL_80: + m_sQueueSample.m_nFrequency = 20000; + LABEL_81: + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_4 = iSound; + processed = 1; + ++iSound; + m_sQueueSample.field_16 = 3; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_fSoundIntensity = 30.0f; + maxDist = 900.f; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_nLoopStart = 0; + emittingVol = m_anRandomTable[3] % 26 + 100; + m_sQueueSample.m_nLoopEnd = -1; + m_sQueueSample.m_bEmittingVolume = emittingVol; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.field_56 = 1; + m_sQueueSample.m_bRequireReflection = 1; + goto LABEL_84; + case SOUND_WEAPON_BAT_ATTACK: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_PED_HIT_BY_BAT; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_4 = iSound++; + processed = 1; + m_sQueueSample.m_nFrequency = RandomDisplacement(2000u) + 22000; + m_sQueueSample.field_16 = 3; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_fSoundIntensity = 30.0f; + maxDist = 900.f; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_nLoopStart = 0; + m_sQueueSample.m_nLoopEnd = -1; + emittingVol = m_anRandomTable[2] % 20 + 100; + m_sQueueSample.m_bEmittingVolume = emittingVol; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.field_56 = 1; + if(m_bDynamicAcousticModelingStatus) + m_sQueueSample.m_bRequireReflection = 1; + else + noReflection = 1; + goto LABEL_84; + case SOUND_WEAPON_SHOT_FIRED: + weapon = &ped->m_weapons[ped->m_currentWeapon]; + switch(weapon->m_eWeaponType) { + case WEAPONTYPE_COLT45: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_WEAPON_PISTOL_SHOT; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_4 = iSound++; + processed = 1; + m_sQueueSample.m_nFrequency = cSampleManager.GetSampleBaseFrequency( + AUDIO_SAMPLE_WEAPON_PISTOL_SHOT); + m_sQueueSample.m_nFrequency += + RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.field_16 = 3; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_fSoundIntensity = 50.0f; + maxDist = 2500.f; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_nLoopStart = 0; + m_sQueueSample.m_nLoopEnd = -1; + emittingVol = m_anRandomTable[1] % 10 + 90; + m_sQueueSample.m_bEmittingVolume = emittingVol; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.field_56 = 1; + if(m_bDynamicAcousticModelingStatus) + m_sQueueSample.m_bRequireReflection = 1; + else + noReflection = 1; + goto LABEL_84; + case WEAPONTYPE_UZI: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_WEAPON_UZI_SHOT; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_4 = iSound++; + processed = 1; + m_sQueueSample.m_nFrequency = cSampleManager.GetSampleBaseFrequency( + AUDIO_SAMPLE_WEAPON_UZI_SHOT); + m_sQueueSample.m_nFrequency += + RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.field_16 = 3; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_fSoundIntensity = 80.0f; + maxDist = 6400.f; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_nLoopStart = 0; + emittingVol = m_anRandomTable[3] % 15 + 70; + m_sQueueSample.m_nLoopEnd = -1; + m_sQueueSample.m_bEmittingVolume = emittingVol; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.field_56 = 1; + goto LABEL_84; + case WEAPONTYPE_SHOTGUN: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_WEAPON_SHOTGUN_SHOT; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_4 = iSound++; + processed = 1; + m_sQueueSample.m_nFrequency = cSampleManager.GetSampleBaseFrequency( + AUDIO_SAMPLE_WEAPON_SHOTGUN_SHOT); + m_sQueueSample.m_nFrequency += + RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.field_16 = 3; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_fSoundIntensity = 60.0f; + maxDist = 3600.f; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_nLoopStart = 0; + m_sQueueSample.m_nLoopEnd = -1; + emittingVol = m_anRandomTable[2] % 10 + 100; + m_sQueueSample.m_bEmittingVolume = emittingVol; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.field_56 = 1; + if(m_bDynamicAcousticModelingStatus) + m_sQueueSample.m_bRequireReflection = 1; + else + noReflection = 1; + goto LABEL_84; + case WEAPONTYPE_AK47: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_WEAPON_CHAINGUN_SHOT; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_4 = iSound++; + processed = 1; + m_sQueueSample.m_nFrequency = cSampleManager.GetSampleBaseFrequency( + AUDIO_SAMPLE_WEAPON_CHAINGUN_SHOT); + m_sQueueSample.m_nFrequency += + RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.field_16 = 3; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_fSoundIntensity = 80.0f; + maxDist = 6400.f; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_nLoopStart = 0; + emittingVol = m_anRandomTable[1] % 15 + 70; + m_sQueueSample.m_nLoopEnd = -1; + m_sQueueSample.m_bEmittingVolume = emittingVol; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.field_56 = 1; + goto LABEL_84; + case WEAPONTYPE_M16: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_WEAPON_M16_SHOT; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_4 = iSound++; + processed = 1; + m_sQueueSample.m_nFrequency = cSampleManager.GetSampleBaseFrequency( + AUDIO_SAMPLE_WEAPON_M16_SHOT); + m_sQueueSample.m_nFrequency += + RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.field_16 = 3; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_fSoundIntensity = 80.0f; + maxDist = 6400.f; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_nLoopStart = 0; + emittingVol = m_anRandomTable[4] % 15 + 70; + m_sQueueSample.m_nLoopEnd = -1; + m_sQueueSample.m_bEmittingVolume = emittingVol; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.field_56 = 1; + goto LABEL_84; + case WEAPONTYPE_SNIPERRIFLE: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_WEAPON_SNIPER_SHOT; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_4 = iSound++; + processed = 1; + m_sQueueSample.m_nFrequency = cSampleManager.GetSampleBaseFrequency( + AUDIO_SAMPLE_WEAPON_SNIPER_SHOT); + m_sQueueSample.m_nFrequency += + RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.field_16 = 3; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_fSoundIntensity = 60.0f; + maxDist = 3600.f; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_nLoopStart = 0; + m_sQueueSample.m_nLoopEnd = -1; + emittingVol = m_anRandomTable[4] % 10 + 110; + m_sQueueSample.m_bEmittingVolume = emittingVol; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.field_56 = 1; + if(m_bDynamicAcousticModelingStatus) + m_sQueueSample.m_bRequireReflection = 1; + else + noReflection = 1; + goto LABEL_84; + case WEAPONTYPE_ROCKETLAUNCHER: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_WEAPON_ROCKET_SHOT; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_4 = iSound++; + processed = 1; + m_sQueueSample.m_nFrequency = cSampleManager.GetSampleBaseFrequency( + AUDIO_SAMPLE_WEAPON_ROCKET_SHOT); + m_sQueueSample.m_nFrequency += + RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.field_16 = 1; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_fSoundIntensity = 90.0f; + maxDist = 8100.f; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_nLoopStart = 0; + m_sQueueSample.m_nLoopEnd = -1; + emittingVol = m_anRandomTable[0] % 20 + 80; + m_sQueueSample.m_bEmittingVolume = emittingVol; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.field_56 = 1; + if(m_bDynamicAcousticModelingStatus) + m_sQueueSample.m_bRequireReflection = 1; + else + noReflection = 1; + goto LABEL_84; + case WEAPONTYPE_FLAMETHROWER: + m_sQueueSample.m_nSampleIndex = + AUDIO_SAMPLE_WEAPON_FLAMETHROWER_SHOT; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_4 = 9; + emittingVol = 90; + m_sQueueSample.m_nFrequency = + (10 * m_sQueueSample.m_nEntityIndex & 2047) + + cSampleManager.GetSampleBaseFrequency( + AUDIO_SAMPLE_WEAPON_FLAMETHROWER_SHOT); + m_sQueueSample.field_16 = 3; + m_sQueueSample.field_48 = 4.0f; + m_sQueueSample.m_fSoundIntensity = 60.0f; + maxDist = 3600.f; + m_sQueueSample.m_nLoopCount = 0; + m_sQueueSample.m_nLoopStart = + cSampleManager.GetSampleLoopStartOffset( + m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nLoopEnd = cSampleManager.GetSampleLoopEndOffset( + m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_bEmittingVolume = 90; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.field_56 = 0; + m_sQueueSample.field_76 = 6; + if(m_bDynamicAcousticModelingStatus) + m_sQueueSample.m_bRequireReflection = 1; + else + noReflection = 1; + goto LABEL_84; + default: goto LABEL_100; + } + + break; + case SOUND_WEAPON_RELOAD: + weapon = &ped->m_weapons[ped->m_currentWeapon]; + switch(weapon->m_eWeaponType) { + case WEAPONTYPE_COLT45: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_WEAPON_PISTOL_RELOAD; + emittingVol = 75; + m_sQueueSample.field_4 = iSound++; + processed = 1; + m_sQueueSample.m_nFrequency = + cSampleManager.GetSampleBaseFrequency( + AUDIO_SAMPLE_WEAPON_PISTOL_RELOAD) + + RandomDisplacement(300u); + goto LABEL_63; + case WEAPONTYPE_UZI: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_WEAPON_UZI_RELOAD; + emittingVol = 75; + m_sQueueSample.field_4 = iSound++; + processed = 1; + m_sQueueSample.m_nFrequency = 39243; + goto LABEL_63; + case WEAPONTYPE_SHOTGUN: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_WEAPON_CHAINGUN_RELOAD; + emittingVol = 75; + m_sQueueSample.field_4 = iSound++; + processed = 1; + m_sQueueSample.m_nFrequency = 30290; + goto LABEL_63; + case WEAPONTYPE_AK47: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_WEAPON_CHAINGUN_RELOAD; + emittingVol = 75; + m_sQueueSample.field_4 = iSound++; + processed = 1; + m_sQueueSample.m_nFrequency = cSampleManager.GetSampleBaseFrequency( + AUDIO_SAMPLE_WEAPON_CHAINGUN_RELOAD); + goto LABEL_62; + case WEAPONTYPE_M16: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_WEAPON_UZI_RELOAD; + emittingVol = 75; + m_sQueueSample.field_4 = iSound++; + processed = 1; + m_sQueueSample.m_nFrequency = cSampleManager.GetSampleBaseFrequency( + AUDIO_SAMPLE_WEAPON_UZI_RELOAD); + goto LABEL_62; + case WEAPONTYPE_SNIPERRIFLE: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_WEAPON_SNIPER_RELOAD; + emittingVol = 75; + m_sQueueSample.field_4 = iSound++; + processed = 1; + m_sQueueSample.m_nFrequency = cSampleManager.GetSampleBaseFrequency( + AUDIO_SAMPLE_WEAPON_SNIPER_RELOAD); + goto LABEL_62; + case WEAPONTYPE_ROCKETLAUNCHER: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_WEAPON_ROCKET_RELOAD; + emittingVol = 75; + m_sQueueSample.field_4 = iSound++; + processed = 1; + m_sQueueSample.m_nFrequency = cSampleManager.GetSampleBaseFrequency( + AUDIO_SAMPLE_WEAPON_ROCKET_RELOAD); + LABEL_62: + LABEL_63: + m_sQueueSample.m_nFrequency += RandomDisplacement(300u); + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_16 = 5; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_fSoundIntensity = 30.0f; + maxDist = 900.f; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_nLoopStart = 0; + m_sQueueSample.m_nLoopEnd = -1; + m_sQueueSample.m_bEmittingVolume = 75; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.field_56 = 1; + m_sQueueSample.m_bRequireReflection = 1; + goto LABEL_84; + default: goto LABEL_100; + } + + break; + case SOUND_WEAPON_AK47_BULLET_ECHO: + case SOUND_WEAPON_UZI_BULLET_ECHO: + case SOUND_WEAPON_M16_BULLET_ECHO: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_75; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_4 = iSound++; + processed = 1; + m_sQueueSample.m_nFrequency = + cSampleManager.GetSampleBaseFrequency(AUDIO_SAMPLE_75); + m_sQueueSample.m_nFrequency += + RandomDisplacement(m_sQueueSample.m_nFrequency >> 4); + m_sQueueSample.field_16 = 3; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_fSoundIntensity = 80.0f; + maxDist = 6400.f; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_nLoopStart = 0; + m_sQueueSample.m_nLoopEnd = -1; + emittingVol = m_anRandomTable[4] % 10 + 40; + m_sQueueSample.m_bEmittingVolume = emittingVol; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.field_56 = 1; + if(m_bDynamicAcousticModelingStatus) + m_sQueueSample.m_bRequireReflection = 1; + else + noReflection = 1; + goto LABEL_84; + case SOUND_WEAPON_FLAMETHROWER_FIRE: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_WEAPON_FLAMETHROWER_FIRE; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_4 = iSound++; + m_sQueueSample.m_nFrequency = cSampleManager.GetSampleBaseFrequency( + AUDIO_SAMPLE_WEAPON_FLAMETHROWER_FIRE); + m_sQueueSample.m_nFrequency += + RandomDisplacement(m_sQueueSample.m_nFrequency >> 4); + m_sQueueSample.field_16 = 3; + m_sQueueSample.field_48 = 4.0f; + m_sQueueSample.m_fSoundIntensity = 60.0f; + maxDist = 3600.f; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_nLoopStart = 0; + m_sQueueSample.m_nLoopEnd = -1; + emittingVol = 70; + m_sQueueSample.m_bEmittingVolume = 70; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.field_56 = 1; + goto LABEL_84; + case SOUND_WEAPON_HIT_PED: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_BULLET_HIT_PED; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_4 = iSound++; + processed = 1; + m_sQueueSample.m_nFrequency = + cSampleManager.GetSampleBaseFrequency(AUDIO_SAMPLE_BULLET_HIT_PED); + m_sQueueSample.m_nFrequency += + RandomDisplacement(m_sQueueSample.m_nFrequency >> 3); + m_sQueueSample.field_16 = 7; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_fSoundIntensity = 30.0f; + maxDist = 900.f; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_nLoopStart = 0; + emittingVol = m_anRandomTable[0] % 20 + 90; + m_sQueueSample.m_nLoopEnd = -1; + m_sQueueSample.m_bEmittingVolume = emittingVol; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.field_56 = 1; + goto LABEL_84; + case SOUND_SPLASH: + m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_COLLISION_WATER; + m_sQueueSample.m_bBankIndex = 0; + m_sQueueSample.field_4 = iSound++; + processed = 1; + m_sQueueSample.m_nFrequency = RandomDisplacement(1400u) + 20000; + m_sQueueSample.field_16 = 1; + m_sQueueSample.field_48 = 0.0f; + m_sQueueSample.m_fSoundIntensity = 40.0f; + maxDist = 1600.f; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_nLoopStart = 0; + emittingVol = m_anRandomTable[2] % 30 + 70; + m_sQueueSample.m_nLoopEnd = -1; + m_sQueueSample.m_bEmittingVolume = emittingVol; + m_sQueueSample.m_bIsDistant = 0; + m_sQueueSample.field_56 = 1; + m_sQueueSample.m_bRequireReflection = 1; + LABEL_84: + if(processed && iSound > 60u) iSound = 21; + if(params->m_fDistance < maxDist) { + CalculateDistance((bool *)params, params->m_fDistance); + m_sQueueSample.m_bVolume = + ComputeVolume(emittingVol, m_sQueueSample.m_fSoundIntensity, + m_sQueueSample.m_fDistance); + if(m_sQueueSample.m_bVolume) { + if(noReflection) { + if(0.2f * m_sQueueSample.m_fSoundIntensity > + m_sQueueSample.m_fDistance) { + noReflection = 0; + } else { + m_sQueueSample.m_bIsDistant = 1; + m_sQueueSample.m_bOffset = 0; + } + } + m_sQueueSample.m_bReverbFlag = 1; + AddSampleToRequestedQueue(); + if(noReflection) { + m_sQueueSample.m_bOffset = 127; + ++m_sQueueSample.m_nSampleIndex; + if(m_asAudioEntities[0].m_awAudioEvent + [i + + 20 * m_sQueueSample.m_nEntityIndex] != + 47 || + weapon->m_eWeaponType != + WEAPONTYPE_FLAMETHROWER) { + m_sQueueSample.field_4 = iSound++; + if(iSound > 60u) iSound = 21; + } else { + ++m_sQueueSample.field_4; + } + AddSampleToRequestedQueue(); + } + } + } + break; + default: SetupPedComments(params, sound); break; + } + LABEL_100: + ++i; + } } void @@ -5396,8 +6508,8 @@ cAudioManager::ProcessSawMillScriptObject(uint8 sound) distSquared = GetDistanceSquared(&m_sQueueSample.m_vecPos); if(distSquared < maxDist) { m_sQueueSample.m_fDistance = sqrt(distSquared); - m_sQueueSample.m_bVolume = cAudioManager::ComputeVolume( - 30u, m_sQueueSample.m_fSoundIntensity, m_sQueueSample.m_fDistance); + m_sQueueSample.m_bVolume = ComputeVolume(30u, m_sQueueSample.m_fSoundIntensity, + m_sQueueSample.m_fDistance); if(m_sQueueSample.m_bVolume) { m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_SAWMILL_1; m_sQueueSample.m_bBankIndex = 0; @@ -5420,7 +6532,7 @@ cAudioManager::ProcessSawMillScriptObject(uint8 sound) } time = CTimer::GetTimeInMilliseconds(); if(time > audioLogicTimers[1]) { - m_sQueueSample.m_bVolume = cAudioManager::ComputeVolume( + m_sQueueSample.m_bVolume = ComputeVolume( 70u, m_sQueueSample.m_fSoundIntensity, m_sQueueSample.m_fDistance); if(m_sQueueSample.m_bVolume) { m_sQueueSample.m_nSampleIndex = AUDIO_SAMPLE_SAWMILL_2; @@ -5444,11 +6556,19 @@ cAudioManager::ProcessSawMillScriptObject(uint8 sound) } } -WRAPPER void cAudioManager::ProcessScriptObject(int32 id) { - EAXJMP(0x576070); + cAudioScriptObject *entity = (cAudioScriptObject *)m_asAudioEntities[id].m_pEntity; + if(entity) { + m_sQueueSample.m_vecPos.x = entity->m_vecPos.x; + m_sQueueSample.m_vecPos.y = entity->m_vecPos.y; + m_sQueueSample.m_vecPos.z = entity->m_vecPos.z; + if(m_asAudioEntities[id].field_24 == 1) + ProcessOneShotScriptObject(m_asAudioEntities[id].m_awAudioEvent[0]); + else + ProcessLoopingScriptObject(entity->m_wSound); + } } void @@ -5490,7 +6610,7 @@ cAudioManager::ProcessShopScriptObject(uint8 sound) SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_bReverbFlag = 1; m_sQueueSample.m_bRequireReflection = 0; - cAudioManager::AddSampleToRequestedQueue(); + AddSampleToRequestedQueue(); } time = CTimer::GetTimeInMilliseconds(); if(time > audioLogicTimers[2]) { @@ -5579,7 +6699,7 @@ cAudioManager::ProcessWorkShopScriptObject(uint8 sound) case SCRIPT_SOUND_WORK_SHOP_LOOP_S: case SCRIPT_SOUND_WORK_SHOP_LOOP_L: maxDist = 400.f; - m_sQueueSample.m_fSoundIntensity = 20.0; + m_sQueueSample.m_fSoundIntensity = 20.0f; break; default: break; } @@ -5598,7 +6718,7 @@ cAudioManager::ProcessWorkShopScriptObject(uint8 sound) m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.field_56 = 0; m_sQueueSample.field_16 = 5; - m_sQueueSample.field_48 = 2.0; + m_sQueueSample.field_48 = 2.0f; m_sQueueSample.m_bEmittingVolume = 30; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); @@ -5611,6 +6731,136 @@ cAudioManager::ProcessWorkShopScriptObject(uint8 sound) } } +void +cAudioManager::SetupPedComments(cPedParams *params, uint32 sound) +{ + CPed *ped = params->m_pPed; + uint8 emittingVol; + float maxDist; + float soundIntensity; + tPedComment pedComment; + + if(ped) { + switch(sound) { + case SOUND_AMMUNATION_WELCOME_1: + pedComment.m_nSampleIndex = AUDIO_SAMPLE_AMMUNATION_WELCOME_1; + break; + case SOUND_AMMUNATION_WELCOME_2: + pedComment.m_nSampleIndex = AUDIO_SAMPLE_AMMUNATION_WELCOME_2; + break; + case SOUND_AMMUNATION_WELCOME_3: + pedComment.m_nSampleIndex = AUDIO_SAMPLE_AMMUNATION_WELCOME_3; + break; + default: + pedComment.m_nSampleIndex = GetPedCommentSfx(ped, sound); + if(pedComment.m_nSampleIndex == NO_SAMPLE) return; + break; + } + + maxDist = 2500.f; + soundIntensity = 50.f; + + if(params->m_fDistance < maxDist) { + CalculateDistance((bool *)params, params->m_fDistance); + if(sound != SOUND_PAGER) { + switch(sound) { + case SOUND_AMMUNATION_WELCOME_1: + case SOUND_AMMUNATION_WELCOME_2: + case SOUND_AMMUNATION_WELCOME_3: emittingVol = maxVolume; break; + default: + if(CWorld::GetIsLineOfSightClear( + TheCamera.GetGameCamPosition(), + m_sQueueSample.m_vecPos, 1, 0, 0, 0, 0, 0, 0)) { + emittingVol = maxVolume; + } else { + emittingVol = 31; + } + break; + } + m_sQueueSample.m_bVolume = ComputeVolume( + emittingVol, soundIntensity, m_sQueueSample.m_fDistance); + pedComment.field_25 = 10; + if(m_sQueueSample.m_bVolume) { + pedComment.field_4 = m_sQueueSample.m_nEntityIndex; + pedComment.m_vecPos.x = m_sQueueSample.m_vecPos.x; + pedComment.m_vecPos.y = m_sQueueSample.m_vecPos.y; + pedComment.m_vecPos.z = m_sQueueSample.m_vecPos.z; + pedComment.m_fDistance = m_sQueueSample.m_fDistance; + pedComment.m_bVolume = m_sQueueSample.m_bVolume; + m_sPedComments.Add(&pedComment); + } + } + } + } else { + switch(sound) { + case SOUND_PED_HELI_PLAYER_FOUND: + maxDist = 160000.f; + soundIntensity = 400.f; + pedComment.m_nSampleIndex = + m_anRandomTable[m_sQueueSample.m_nEntityIndex & 3] % 29u + + AUDIO_SAMPLE_POLICE_HELI_FOUND_PLAYER_1; + break; + case SOUND_PED_BODYCAST_HIT: + if(CTimer::GetTimeInMilliseconds() <= audioLogicTimers[8]) return; + maxDist = 2500.f; + soundIntensity = 50.f; + audioLogicTimers[8] = CTimer::GetTimeInMilliseconds() + 500; + pedComment.m_nSampleIndex = + (m_anRandomTable[m_sQueueSample.m_nEntityIndex & 3] & 3) + + AUDIO_SAMPLE_PED_BODYCAST_HIT_1; + break; + case SOUND_INJURED_PED_MALE_OUCH: + case SOUND_8A: + maxDist = 2500.f; + soundIntensity = 50.f; + pedComment.m_nSampleIndex = + m_anRandomTable[m_sQueueSample.m_nEntityIndex & 3] % 15u + + AUDIO_SAMPLE_INJURED_PED_MALE_OUCH_1; + break; + case SOUND_INJURED_PED_FEMALE: + maxDist = 2500.f; + soundIntensity = 50.f; + pedComment.m_nSampleIndex = + m_anRandomTable[m_sQueueSample.m_nEntityIndex & 3] % 11u + + AUDIO_SAMPLE_INJURED_PED_FEMALE_OUCH_1; + break; + default: return; + } + + if(params->m_fDistance < maxDist) { + CalculateDistance((bool *)params, params->m_fDistance); + if(sound != SOUND_PAGER) { + switch(sound) { + case SOUND_AMMUNATION_WELCOME_1: + case SOUND_AMMUNATION_WELCOME_2: + case SOUND_AMMUNATION_WELCOME_3: emittingVol = maxVolume; break; + default: + if(CWorld::GetIsLineOfSightClear( + TheCamera.GetGameCamPosition(), + m_sQueueSample.m_vecPos, 1, 0, 0, 0, 0, 0, 0)) { + emittingVol = maxVolume; + } else { + emittingVol = 31; + } + break; + } + m_sQueueSample.m_bVolume = ComputeVolume( + emittingVol, soundIntensity, m_sQueueSample.m_fDistance); + pedComment.field_25 = 10; + if(m_sQueueSample.m_bVolume) { + pedComment.field_4 = m_sQueueSample.m_nEntityIndex; + pedComment.m_vecPos.x = m_sQueueSample.m_vecPos.x; + pedComment.m_vecPos.y = m_sQueueSample.m_vecPos.y; + pedComment.m_vecPos.z = m_sQueueSample.m_vecPos.z; + pedComment.m_fDistance = m_sQueueSample.m_fDistance; + pedComment.m_bVolume = m_sQueueSample.m_bVolume; + m_sPedComments.Add(&pedComment); + } + } + } + } +} + WRAPPER void cAudioManager::Service() { @@ -5663,6 +6913,8 @@ WRAPPER void cAudioManager::PreloadMissionAudio(char *) } STARTPATCHES +InjectHook(0x5755C0, &cPedComments::Add, PATCH_JUMP); + InjectHook(0x57B210, &cAudioManager::AddDetailsToRequestedOrderList, PATCH_JUMP); InjectHook(0x56AD30, &cAudioManager::AddPlayerCarSample, PATCH_JUMP); InjectHook(0x57B300, &cAudioManager::AddReflectionsToRequestedQueue, PATCH_JUMP); @@ -5842,6 +7094,7 @@ InjectHook(0x579170, &cAudioManager::ProcessBridgeWarning, PATCH_JUMP); InjectHook(0x577CA0, &cAudioManager::ProcessCinemaScriptObject, PATCH_JUMP); InjectHook(0x577E50, &cAudioManager::ProcessDocksScriptObject, PATCH_JUMP); InjectHook(0x569870, &cAudioManager::ProcessEntity, PATCH_JUMP); +InjectHook(0x575AC0, &cAudioManager::ProcessExplosions, PATCH_JUMP); InjectHook(0x578FD0, &cAudioManager::ProcessFireHydrant, PATCH_JUMP); InjectHook(0x5785E0, &cAudioManager::ProcessFrontEnd, PATCH_JUMP); InjectHook(0x577FE0, &cAudioManager::ProcessHomeScriptObject, PATCH_JUMP); @@ -5854,10 +7107,16 @@ InjectHook(0x56EC00, &cAudioManager::ProcessJumboTakeOff, PATCH_JUMP); InjectHook(0x56EA10, &cAudioManager::ProcessJumboTaxi, PATCH_JUMP); InjectHook(0x5777E0, &cAudioManager::ProcessLaunderetteScriptObject, PATCH_JUMP); InjectHook(0x576770, &cAudioManager::ProcessLoopingScriptObject, PATCH_JUMP); +InjectHook(0x5760C0, &cAudioManager::ProcessOneShotScriptObject, PATCH_JUMP); +InjectHook(0x56F450, &cAudioManager::ProcessPed, PATCH_JUMP); +InjectHook(0x56F4D0, &cAudioManager::ProcessPedHeadphones, PATCH_JUMP); +InjectHook(0x56F650, &cAudioManager::ProcessPedOneShots, PATCH_JUMP); InjectHook(0x5699C0, &cAudioManager::ProcessPhysical, PATCH_JUMP); InjectHook(0x577280, &cAudioManager::ProcessPornCinema, PATCH_JUMP); InjectHook(0x577630, &cAudioManager::ProcessSawMillScriptObject, PATCH_JUMP); +InjectHook(0x576070, &cAudioManager::ProcessScriptObject, PATCH_JUMP); InjectHook(0x577970, &cAudioManager::ProcessShopScriptObject, PATCH_JUMP); InjectHook(0x5697D0, &cAudioManager::ProcessSpecial, PATCH_JUMP); InjectHook(0x577530, &cAudioManager::ProcessWorkShopScriptObject, PATCH_JUMP); +InjectHook(0x570690, &cAudioManager::SetupPedComments, PATCH_JUMP); ENDPATCHES diff --git a/src/audio/AudioManager.h b/src/audio/AudioManager.h index 0d4a6f30..5b74cc16 100644 --- a/src/audio/AudioManager.h +++ b/src/audio/AudioManager.h @@ -51,7 +51,7 @@ public: uint8 field_91; }; -static_assert(sizeof(tActiveSample) == 0x5c, "tActiveSample: error"); +static_assert(sizeof(tActiveSample) == 92, "tActiveSample: error"); enum eAudioType : int32 { AUDIOTYPE_PHYSICAL = 0, @@ -83,11 +83,11 @@ public: int16 m_awAudioEvent[4]; uint8 gap_18[2]; float m_afVolume[4]; - uint8 field_24; + uint8 field_24; // is looping uint8 field_25[3]; }; -static_assert(sizeof(tAudioEntity) == 0x28, "tAudioEntity: error"); +static_assert(sizeof(tAudioEntity) == 40, "tAudioEntity: error"); class tPedComment { @@ -101,19 +101,21 @@ public: uint8 gap_26[2]; }; -static_assert(sizeof(tPedComment) == 0x1c, "tPedComment: error"); +static_assert(sizeof(tPedComment) == 28, "tPedComment: error"); class cPedComments { public: - tPedComment m_asPedComments[40]; - uint8 field_1120[40]; - uint8 field_1160[2]; - uint8 field_1162; + tPedComment m_asPedComments[2][20]; + uint8 field_1120[2][20]; + uint8 nrOfCommentsInBank[2]; + uint8 activeBank; uint8 gap_1163[1]; + + void Add(tPedComment *com); // test }; -static_assert(sizeof(cPedComments) == 0x48c, "cPedComments: error"); +static_assert(sizeof(cPedComments) == 1164, "cPedComments: error"); class CEntity; @@ -133,7 +135,7 @@ public: int32 m_nBaseVolume; }; -static_assert(sizeof(cAudioCollision) == 0x28, "cAudioCollision: error"); +static_assert(sizeof(cAudioCollision) == 40, "cAudioCollision: error"); class cAudioCollisionManager { @@ -146,7 +148,7 @@ public: cAudioCollision m_sQueue; }; -static_assert(sizeof(cAudioCollisionManager) == 0x354, "cAudioCollisionManager: error"); +static_assert(sizeof(cAudioCollisionManager) == 852, "cAudioCollisionManager: error"); class cMissionAudio { @@ -166,12 +168,13 @@ public: uint8 field_31; }; -static_assert(sizeof(cMissionAudio) == 0x20, "cMissionAudio: error"); +static_assert(sizeof(cMissionAudio) == 32, "cMissionAudio: error"); class cVehicleParams; class CPlane; class CVehicle; class CPed; +class cPedParams; class cAudioScriptObject { public: @@ -182,11 +185,11 @@ public: static void *operator new(size_t); static void *operator new(size_t, int); - static void operator delete(void*, size_t); - static void operator delete(void*, int); + static void operator delete(void *, size_t); + static void operator delete(void *, int); }; -static_assert(sizeof(cAudioScriptObject) == 0x14, "cAudioScriptObject: error"); +static_assert(sizeof(cAudioScriptObject) == 20, "cAudioScriptObject: error"); enum { @@ -334,7 +337,7 @@ public: bool MissionScriptAudioUsesPoliceChannel(int32 soundMission); - char* Get3DProviderName(uint8 id); + char *Get3DProviderName(uint8 id); bool SetupJumboFlySound(uint8 emittingVol); /// ok bool SetupJumboRumbleSound(uint8 emittingVol); /// ok @@ -474,7 +477,7 @@ public: void ProcessDocksScriptObject(uint8 sound); /// ok // bool ProcessEngineDamage(void *); //todo requires CVehicle void ProcessEntity(int32 sound); /// ok - void ProcessExplosions(int32 explosion); // todo requires CExplosion + void ProcessExplosions(int32 explosion); // test void ProcessFireHydrant(); /// ok void ProcessFires(int32 entity); // todo requires gFireManager void ProcessFrontEnd(); /// ok @@ -492,12 +495,12 @@ public: void ProcessLoopingScriptObject(uint8 sound); /// ok // void ProcessMissionAudio(); // void ProcessModelVehicle(void *); - // void ProcessOneShotScriptObject(uint8 sound); - void ProcessPed(CPhysical *p); // todo - // void ProcessPedHeadphones(void *); - // void ProcessPedOneShots(void *); - void ProcessPhysical(int32 id); /// ok - void ProcessPlane(void *); // todo + void ProcessOneShotScriptObject(uint8 sound); // test + void ProcessPed(CPhysical *ped); // test + void ProcessPedHeadphones(cPedParams *params); // test + void ProcessPedOneShots(cPedParams *params); // test, remove goto + void ProcessPhysical(int32 id); /// ok + void ProcessPlane(void *); // todo // void ProcessPlayersVehicleEngine(void *, void *); void ProcessPoliceCellBeatingScriptObject(uint8 sound); // todo void ProcessPornCinema(uint8 sound); /// ok @@ -506,7 +509,7 @@ public: // void ProcessReverb(); // bool ProcessReverseGear(void *); void ProcessSawMillScriptObject(uint8 sound); /// ok - void ProcessScriptObject(int32 id); // todo + void ProcessScriptObject(int32 id); // test void ProcessShopScriptObject(uint8 sound); /// ok void ProcessSpecial(); /// ok // bool ProcessTrainNoise(void *); @@ -523,19 +526,10 @@ public: void ProcessWeather(int32 id); // todo // bool ProcessWetRoadNoise(void *); void ProcessWorkShopScriptObject(uint8 sound); /// ok - - - void PlayOneShot(int, unsigned short, float); - void SetEffectsFadeVol(unsigned char); - void SetMusicFadeVol(unsigned char); - int8 SetCurrent3DProvider(unsigned char); - void ReportCrime(eCrimeType, CVector const &); - void PlaySuspectLastSeen(float, float, float); - void ReportCollision(CEntity *, CEntity *, unsigned char, unsigned char, float, float); - void ResetTimers(unsigned int); - void PreloadMissionAudio(char *); + + void SetupPedComments(cPedParams *params, uint32 sound); // todo hook }; -static_assert(sizeof(cAudioManager) == 0x4B14, "cAudioManager: error"); +static_assert(sizeof(cAudioManager) == 19220, "cAudioManager: error"); extern cAudioManager &AudioManager; diff --git a/src/audio/DMAudio.h b/src/audio/DMAudio.h index 318b9947..0932e581 100644 --- a/src/audio/DMAudio.h +++ b/src/audio/DMAudio.h @@ -1,4 +1,7 @@ #pragma once + +#include "Wanted.h" + enum eSound : int16 { SOUND_CAR_DOOR_CLOSE_BONNET = 0, diff --git a/src/control/PathFind.cpp b/src/control/PathFind.cpp index e9b33395..87ce4476 100644 --- a/src/control/PathFind.cpp +++ b/src/control/PathFind.cpp @@ -207,8 +207,8 @@ CPathFind::PreparePathData(void) numExtern++; if(InfoForTileCars[k].numLeftLanes + InfoForTileCars[k].numRightLanes > numLanes) numLanes = InfoForTileCars[k].numLeftLanes + InfoForTileCars[k].numRightLanes; - maxX = max(maxX, Abs(InfoForTileCars[k].x)); - maxY = max(maxY, Abs(InfoForTileCars[k].y)); + maxX = Max(maxX, Abs(InfoForTileCars[k].x)); + maxY = Max(maxY, Abs(InfoForTileCars[k].y)); }else if(InfoForTileCars[k].type == NodeTypeIntern) numIntern++; } @@ -392,7 +392,7 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor if(Abs(dx) < nearestDist){ dy = tempnodes[k].pos.y - CoorsXFormed.y; if(Abs(dy) < nearestDist){ - nearestDist = max(Abs(dx), Abs(dy)); + nearestDist = Max(Abs(dx), Abs(dy)); nearestId = k; } } @@ -501,13 +501,13 @@ CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoFor // Find i inside path segment iseg = 0; - for(j = max(oldNumPathNodes, i-12); j < i; j++) + for(j = Max(oldNumPathNodes, i-12); j < i; j++) if(m_pathNodes[j].objectIndex == m_pathNodes[i].objectIndex) iseg++; istart = 12*m_mapObjects[m_pathNodes[i].objectIndex]->m_modelIndex; // Add links to other internal nodes - for(j = max(oldNumPathNodes, i-12); j < min(m_numPathNodes, i+12); j++){ + for(j = Max(oldNumPathNodes, i-12); j < Min(m_numPathNodes, i+12); j++){ if(m_pathNodes[i].objectIndex != m_pathNodes[j].objectIndex || i == j) continue; // N.B.: in every path segment, the externals have to be at the end diff --git a/src/control/Population.h b/src/control/Population.h index 7e4b40d8..3582ccef 100644 --- a/src/control/Population.h +++ b/src/control/Population.h @@ -1,8 +1,9 @@ #pragma once +#include "Game.h" + class CPed; class CVehicle; -enum eLevelName; struct PedGroup { diff --git a/src/control/Replay.cpp b/src/control/Replay.cpp index 12636d32..207845b9 100644 --- a/src/control/Replay.cpp +++ b/src/control/Replay.cpp @@ -366,8 +366,8 @@ void CReplay::StorePedAnimation(CPed *ped, CStoredAnimationState *state) CAnimBlendAssociation* main = RpAnimBlendClumpGetMainAssociation((RpClump*)ped->m_rwObject, &second, &blend_amount); if (main){ state->animId = main->animId; - state->time = 255.0f / 4.0f * max(0.0f, min(4.0f, main->currentTime)); - state->speed = 255.0f / 3.0f * max(0.0f, min(3.0f, main->speed)); + state->time = 255.0f / 4.0f * Max(0.0f, Min(4.0f, main->currentTime)); + state->speed = 255.0f / 3.0f * Max(0.0f, Min(3.0f, main->speed)); }else{ state->animId = 3; state->time = 0; @@ -375,9 +375,9 @@ void CReplay::StorePedAnimation(CPed *ped, CStoredAnimationState *state) } if (second) { state->secAnimId = second->animId; - state->secTime = 255.0f / 4.0f * max(0.0f, min(4.0f, second->currentTime)); - state->secSpeed = 255.0f / 3.0f * max(0.0f, min(3.0f, second->speed)); - state->blendAmount = 255.0f / 2.0f * max(0.0f, min(2.0f, blend_amount)); + state->secTime = 255.0f / 4.0f * Max(0.0f, Min(4.0f, second->currentTime)); + state->secSpeed = 255.0f / 3.0f * Max(0.0f, Min(3.0f, second->speed)); + state->blendAmount = 255.0f / 2.0f * Max(0.0f, Min(2.0f, blend_amount)); }else{ state->secAnimId = 0; state->secTime = 0; @@ -387,9 +387,9 @@ void CReplay::StorePedAnimation(CPed *ped, CStoredAnimationState *state) CAnimBlendAssociation* partial = RpAnimBlendClumpGetMainPartialAssociation((RpClump*)ped->m_rwObject); if (partial) { state->partAnimId = partial->animId; - state->partAnimTime = 255.0f / 4.0f * max(0.0f, min(4.0f, partial->currentTime)); - state->partAnimSpeed = 255.0f / 3.0f * max(0.0f, min(3.0f, partial->speed)); - state->partBlendAmount = 255.0f / 2.0f * max(0.0f, min(2.0f, partial->blendAmount)); + state->partAnimTime = 255.0f / 4.0f * Max(0.0f, Min(4.0f, partial->currentTime)); + state->partAnimSpeed = 255.0f / 3.0f * Max(0.0f, Min(3.0f, partial->speed)); + state->partBlendAmount = 255.0f / 2.0f * Max(0.0f, Min(2.0f, partial->blendAmount)); }else{ state->partAnimId = 0; state->partAnimTime = 0; @@ -408,9 +408,9 @@ void CReplay::StoreDetailedPedAnimation(CPed *ped, CStoredDetailedAnimationState CAnimBlendAssociation* assoc = RpAnimBlendClumpGetMainAssociation_N((RpClump*)ped->m_rwObject, i); if (assoc){ state->aAnimId[i] = assoc->animId; - state->aCurTime[i] = 255.0f / 4.0f * max(0.0f, min(4.0f, assoc->currentTime)); - state->aSpeed[i] = 255.0f / 3.0f * max(0.0f, min(3.0f, assoc->speed)); - state->aBlendAmount[i] = 255.0f / 2.0f * max(0.0f, min(2.0f, assoc->blendAmount)); + state->aCurTime[i] = 255.0f / 4.0f * Max(0.0f, Min(4.0f, assoc->currentTime)); + state->aSpeed[i] = 255.0f / 3.0f * Max(0.0f, Min(3.0f, assoc->speed)); + state->aBlendAmount[i] = 255.0f / 2.0f * Max(0.0f, Min(2.0f, assoc->blendAmount)); state->aFlags[i] = assoc->flags; if (assoc->callbackType == CAnimBlendAssociation::CB_FINISH || assoc->callbackType == CAnimBlendAssociation::CB_DELETE) { state->aFunctionCallbackID[i] = FindCBFunctionID(assoc->callback); @@ -431,9 +431,9 @@ void CReplay::StoreDetailedPedAnimation(CPed *ped, CStoredDetailedAnimationState CAnimBlendAssociation* assoc = RpAnimBlendClumpGetMainPartialAssociation_N((RpClump*)ped->m_rwObject, i); if (assoc) { state->aAnimId2[i] = assoc->animId; - state->aCurTime2[i] = 255.0f / 4.0f * max(0.0f, min(4.0f, assoc->currentTime)); - state->aSpeed2[i] = 255.0f / 3.0f * max(0.0f, min(3.0f, assoc->speed)); - state->aBlendAmount2[i] = 255.0f / 2.0f * max(0.0f, min(2.0f, assoc->blendAmount)); + state->aCurTime2[i] = 255.0f / 4.0f * Max(0.0f, Min(4.0f, assoc->currentTime)); + state->aSpeed2[i] = 255.0f / 3.0f * Max(0.0f, Min(3.0f, assoc->speed)); + state->aBlendAmount2[i] = 255.0f / 2.0f * Max(0.0f, Min(2.0f, assoc->blendAmount)); state->aFlags2[i] = assoc->flags; if (assoc->callbackType == CAnimBlendAssociation::CB_FINISH || assoc->callbackType == CAnimBlendAssociation::CB_DELETE) { state->aFunctionCallbackID2[i] = FindCBFunctionID(assoc->callback); @@ -625,9 +625,9 @@ void CReplay::StoreCarUpdate(CVehicle *vehicle, int id) vp->health = vehicle->m_fHealth / 4.0f; /* Not anticipated that health can be > 1000. */ vp->acceleration = vehicle->m_fGasPedal * 100.0f; vp->panels = vehicle->IsCar() ? ((CAutomobile*)vehicle)->Damage.m_panelStatus : 0; - vp->velocityX = 8000.0f * max(-4.0f, min(4.0f, vehicle->GetMoveSpeed().x)); /* 8000!? */ - vp->velocityY = 8000.0f * max(-4.0f, min(4.0f, vehicle->GetMoveSpeed().y)); - vp->velocityZ = 8000.0f * max(-4.0f, min(4.0f, vehicle->GetMoveSpeed().z)); + vp->velocityX = 8000.0f * Max(-4.0f, Min(4.0f, vehicle->GetMoveSpeed().x)); /* 8000!? */ + vp->velocityY = 8000.0f * Max(-4.0f, Min(4.0f, vehicle->GetMoveSpeed().y)); + vp->velocityZ = 8000.0f * Max(-4.0f, Min(4.0f, vehicle->GetMoveSpeed().z)); vp->mi = vehicle->GetModelIndex(); vp->primary_color = vehicle->m_currentColour1; vp->secondary_color = vehicle->m_currentColour2; @@ -1501,9 +1501,9 @@ void CReplay::ProcessLookAroundCam(void) --FramesActiveLookAroundCam; fBetaAngleLookAroundCam += x_moved; if (CPad::NewMouseControllerState.LMB && CPad::NewMouseControllerState.RMB) - fDistanceLookAroundCam = max(3.0f, min(15.0f, fDistanceLookAroundCam + 2.0f * y_moved)); + fDistanceLookAroundCam = Max(3.0f, Min(15.0f, fDistanceLookAroundCam + 2.0f * y_moved)); else - fAlphaAngleLookAroundCam = max(0.1f, min(1.5f, fAlphaAngleLookAroundCam + y_moved)); + fAlphaAngleLookAroundCam = Max(0.1f, Min(1.5f, fAlphaAngleLookAroundCam + y_moved)); CVector camera_pt( fDistanceLookAroundCam * Sin(fBetaAngleLookAroundCam) * Cos(fAlphaAngleLookAroundCam), fDistanceLookAroundCam * Cos(fBetaAngleLookAroundCam) * Cos(fAlphaAngleLookAroundCam), diff --git a/src/control/Script.cpp b/src/control/Script.cpp index c81cd050..7e87fc6e 100644 --- a/src/control/Script.cpp +++ b/src/control/Script.cpp @@ -1987,7 +1987,7 @@ int8 CRunningScript::ProcessCommandsFrom100To199(int32 command) car->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS; car->m_status = STATUS_PHYSICS; car->bEngineOn = true; - car->AutoPilot.m_nCruiseSpeed = max(car->AutoPilot.m_nCruiseSpeed, 6); + car->AutoPilot.m_nCruiseSpeed = Max(car->AutoPilot.m_nCruiseSpeed, 6); car->AutoPilot.m_nTimeToStartMission = CTimer::GetTimeInMilliseconds(); return 0; } @@ -1999,7 +1999,7 @@ int8 CRunningScript::ProcessCommandsFrom100To199(int32 command) CCarCtrl::JoinCarWithRoadSystem(car); car->AutoPilot.m_nCarMission = MISSION_CRUISE; car->bEngineOn = true; - car->AutoPilot.m_nCruiseSpeed = max(car->AutoPilot.m_nCruiseSpeed, 6); + car->AutoPilot.m_nCruiseSpeed = Max(car->AutoPilot.m_nCruiseSpeed, 6); car->AutoPilot.m_nTimeToStartMission = CTimer::GetTimeInMilliseconds(); return 0; } @@ -2083,7 +2083,7 @@ int8 CRunningScript::ProcessCommandsFrom100To199(int32 command) CollectParameters(&m_nIp, 2); CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); assert(car); - car->AutoPilot.m_nCruiseSpeed = min(*(float*)&ScriptParams[1], 60.0f * car->pHandling->Transmission.fUnkMaxVelocity); + car->AutoPilot.m_nCruiseSpeed = Min(*(float*)&ScriptParams[1], 60.0f * car->pHandling->Transmission.fUnkMaxVelocity); return 0; } case COMMAND_SET_CAR_DRIVING_STYLE: diff --git a/src/core/Camera.cpp b/src/core/Camera.cpp index cb16c3ad..f15232f3 100644 --- a/src/core/Camera.cpp +++ b/src/core/Camera.cpp @@ -209,7 +209,7 @@ WellBufferMe(float Target, float *CurrentValue, float *CurrentSpeed, float MaxSp else if(TargetSpeed > 0.0f && *CurrentSpeed > TargetSpeed) *CurrentSpeed = TargetSpeed; - *CurrentValue += *CurrentSpeed * min(10.0f, CTimer::GetTimeStep()); + *CurrentValue += *CurrentSpeed * Min(10.0f, CTimer::GetTimeStep()); } void @@ -697,7 +697,7 @@ CCam::Process_FollowPed(const CVector &CameraTarget, float TargetOrientation, fl // Process height offset to avoid peds and cars float TargetZOffSet = m_fUnknownZOffSet + m_fDimensionOfHighestNearCar; - TargetZOffSet = max(TargetZOffSet, m_fPedBetweenCameraHeightOffset); + TargetZOffSet = Max(TargetZOffSet, m_fPedBetweenCameraHeightOffset); float TargetHeight = CameraTarget.z + TargetZOffSet - Source.z; if(TargetHeight > m_fCamBufferedHeight){ @@ -753,7 +753,7 @@ CCam::Process_FollowPed(const CVector &CameraTarget, float TargetOrientation, fl } } - TargetCoors.z += min(1.0f, m_fCamBufferedHeight/2.0f); + TargetCoors.z += Min(1.0f, m_fCamBufferedHeight/2.0f); m_cvecTargetCoorsForFudgeInter = TargetCoors; Front = TargetCoors - Source; @@ -991,7 +991,7 @@ CCam::WorkOutCamHeight(const CVector &TargetCoors, float TargetOrientation, floa } if(FoundCamRoof){ // Camera is under something - float roof = FoundRoofCenter ? min(CamRoof, CarRoof) : CamRoof; + float roof = FoundRoofCenter ? Min(CamRoof, CarRoof) : CamRoof; // Same weirdness again? TargetAlpha = CGeneral::GetATanOfXY(CA_MAX_DISTANCE, roof - CamTargetZ - 1.5f); CamClear = false; @@ -1249,7 +1249,7 @@ void CCam::Cam_On_A_String_Unobscured(const CVector &TargetCoors, float BaseDist) { CA_MAX_DISTANCE = BaseDist + 0.1f + TheCamera.CarZoomValueSmooth; - CA_MIN_DISTANCE = min(BaseDist*0.6f, 3.5f); + CA_MIN_DISTANCE = Min(BaseDist*0.6f, 3.5f); CVector Dist = Source - TargetCoors; diff --git a/src/core/Camera.h b/src/core/Camera.h index c617c3d7..1f38963b 100644 --- a/src/core/Camera.h +++ b/src/core/Camera.h @@ -442,7 +442,7 @@ int m_iModeObbeCamIsInForCar; static bool &m_bUseMouse3rdPerson; bool Get_Just_Switched_Status() { return m_bJust_Switched; } - inline const CMatrix GetCameraMatrix(void) { return m_cameraMatrix; } + inline const CMatrix& GetCameraMatrix(void) { return m_cameraMatrix; } CVector &GetGameCamPosition(void) { return m_vecGameCamPos; } bool IsPointVisible(const CVector ¢er, const CMatrix *mat); bool IsSphereVisible(const CVector ¢er, float radius, const CMatrix *mat); diff --git a/src/core/CdStream.cpp b/src/core/CdStream.cpp index 255e46bb..57b1cbe2 100644 --- a/src/core/CdStream.cpp +++ b/src/core/CdStream.cpp @@ -5,8 +5,8 @@ #include "rwcore.h" #include "RwHelper.h" -#define CDDEBUG(f, ...) debug ("%s: " f "\n", "cdvd_stream", __VA_ARGS__) -#define CDTRACE(f, ...) printf("%s: " f "\n", "cdvd_stream", __VA_ARGS__) +#define CDDEBUG(f, ...) debug ("%s: " f "\n", "cdvd_stream", ## __VA_ARGS__) +#define CDTRACE(f, ...) printf("%s: " f "\n", "cdvd_stream", ## __VA_ARGS__) struct CdReadInfo { diff --git a/src/core/Collision.cpp b/src/core/Collision.cpp index 66b29d9f..538bcae6 100644 --- a/src/core/Collision.cpp +++ b/src/core/Collision.cpp @@ -153,10 +153,10 @@ CCollision::LoadCollisionWhenINeedIt(bool forceChange) // on water we expect to be between levels multipleLevels = true; }else{ - xmin = max(sx - 1, 0); - xmax = min(sx + 1, NUMSECTORS_X-1); - ymin = max(sy - 1, 0); - ymax = min(sy + 1, NUMSECTORS_Y-1); + xmin = Max(sx - 1, 0); + xmax = Min(sx + 1, NUMSECTORS_X-1); + ymin = Max(sy - 1, 0); + ymax = Min(sy + 1, NUMSECTORS_Y-1); for(x = xmin; x <= xmax; x++) for(y = ymin; y <= ymax; y++){ diff --git a/src/core/EventList.cpp b/src/core/EventList.cpp index a833cc8e..caf0cb3f 100644 --- a/src/core/EventList.cpp +++ b/src/core/EventList.cpp @@ -4,7 +4,7 @@ #include "ModelIndices.h" #include "World.h" #include "Wanted.h" -#include "Eventlist.h" +#include "EventList.h" int32 CEventList::ms_nFirstFreeSlotIndex; //CEvent gaEvent[NUMEVENTS]; diff --git a/src/core/Explosion.cpp b/src/core/Explosion.cpp index f55cbcd6..9ccd6e81 100644 --- a/src/core/Explosion.cpp +++ b/src/core/Explosion.cpp @@ -3,3 +3,27 @@ #include "Explosion.h" WRAPPER void CExplosion::AddExplosion(CEntity *explodingEntity, CEntity *culprit, eExplosionType type, const CVector &pos, uint32) { EAXJMP(0x5591C0); } + +WRAPPER +int8 CExplosion::GetExplosionActiveCounter(uint8 id) +{ + EAXJMP(0x559140); +} + +WRAPPER +CVector *CExplosion::GetExplosionPosition(uint8 id) +{ + EAXJMP(0x5591A0); +} + +WRAPPER +uint8 CExplosion::GetExplosionType(uint8 id) +{ + EAXJMP(0x559180); +} + +WRAPPER +void CExplosion::ResetExplosionActiveCounter(uint8 id) +{ + EAXJMP(0x559160); +} diff --git a/src/core/Explosion.h b/src/core/Explosion.h index 93d60ab3..fde4ad7f 100644 --- a/src/core/Explosion.h +++ b/src/core/Explosion.h @@ -1,6 +1,7 @@ #pragma once class CEntity; +class CVector; enum eExplosionType { @@ -19,5 +20,11 @@ enum eExplosionType class CExplosion { public: - static void AddExplosion(CEntity *explodingEntity, CEntity *culprit, eExplosionType type, const CVector &pos, uint32); + static void AddExplosion(CEntity *explodingEntity, CEntity *culprit, eExplosionType type, + const CVector &pos, uint32); + + static int8 GetExplosionActiveCounter(uint8 id); + static CVector *GetExplosionPosition(uint8 id); + static uint8 GetExplosionType(uint8 id); + static void ResetExplosionActiveCounter(uint8 id); }; diff --git a/src/core/Frontend.cpp b/src/core/Frontend.cpp index 1de5c94f..c8a00744 100644 --- a/src/core/Frontend.cpp +++ b/src/core/Frontend.cpp @@ -1030,7 +1030,7 @@ int CMenuManager::FadeIn(int alpha) m_nCurrScreen == MENUPAGE_SAVING_IN_PROGRESS || m_nCurrScreen == MENUPAGE_DELETING) return alpha; - return min(m_nMenuFadeAlpha, alpha); + return Min(m_nMenuFadeAlpha, alpha); } #endif diff --git a/src/core/Game.cpp b/src/core/Game.cpp index e07106ce..b488a217 100644 --- a/src/core/Game.cpp +++ b/src/core/Game.cpp @@ -1,7 +1,7 @@ #include "common.h" #include "patcher.h" #include "Game.h" -#include "Main.h" +#include "main.h" #include "CdStream.h" #include "FileMgr.h" diff --git a/src/core/Instance.h b/src/core/Instance.h index 1038c005..bb74ea84 100644 --- a/src/core/Instance.h +++ b/src/core/Instance.h @@ -7,4 +7,5 @@ class CInstance : CPlaceable { public: + ~CInstance() = default; }; diff --git a/src/core/Pad.cpp b/src/core/Pad.cpp index f334a255..b5086d64 100644 --- a/src/core/Pad.cpp +++ b/src/core/Pad.cpp @@ -299,10 +299,10 @@ CControllerState CPad::ReconcileTwoControllersInput(CControllerState const &Stat { if ( State1.button || State2.button ) ReconState.button = 255; } #define _RECONCILE_AXIS_POSITIVE(axis) \ - { if ( State1.axis >= 0 && State2.axis >= 0 ) ReconState.axis = max(State1.axis, State2.axis); } + { if ( State1.axis >= 0 && State2.axis >= 0 ) ReconState.axis = Max(State1.axis, State2.axis); } #define _RECONCILE_AXIS_NEGATIVE(axis) \ - { if ( State1.axis <= 0 && State2.axis <= 0 ) ReconState.axis = min(State1.axis, State2.axis); } + { if ( State1.axis <= 0 && State2.axis <= 0 ) ReconState.axis = Min(State1.axis, State2.axis); } #define _RECONCILE_AXIS(axis) \ { _RECONCILE_AXIS_POSITIVE(axis); _RECONCILE_AXIS_NEGATIVE(axis); } diff --git a/src/core/Placeable.cpp b/src/core/Placeable.cpp index c1fe705e..d2cec82b 100644 --- a/src/core/Placeable.cpp +++ b/src/core/Placeable.cpp @@ -7,7 +7,7 @@ CPlaceable::CPlaceable(void) m_matrix.SetScale(1.0f); } -CPlaceable::~CPlaceable(void) { } +CPlaceable::~CPlaceable(void) = default; void CPlaceable::SetHeading(float angle) diff --git a/src/core/Streaming.cpp b/src/core/Streaming.cpp index a7bde91e..8158cd1d 100644 --- a/src/core/Streaming.cpp +++ b/src/core/Streaming.cpp @@ -1940,7 +1940,7 @@ CStreaming::ProcessEntitiesInSectorList(CPtrList &list, float x, float y, float CTimeModelInfo *mi = (CTimeModelInfo*)CModelInfo::GetModelInfo(e->GetModelIndex()); if(mi->m_type != MITYPE_TIME || CClock::GetIsTimeInRange(mi->GetTimeOn(), mi->GetTimeOff())){ lodDistSq = sq(mi->GetLargestLodDistance()); - lodDistSq = min(lodDistSq, sq(STREAM_DIST)); + lodDistSq = Min(lodDistSq, sq(STREAM_DIST)); pos = CVector2D(e->GetPosition()); if(xmin < pos.x && pos.x < xmax && ymin < pos.y && pos.y < ymax && @@ -2160,20 +2160,20 @@ CStreaming::DeleteRwObjectsBehindCamera(int32 mem) if(Abs(TheCamera.GetForward().x) > Abs(TheCamera.GetForward().y)){ // looking west/east - ymin = max(iy - 10, 0); - ymax = min(iy + 10, NUMSECTORS_Y); + ymin = Max(iy - 10, 0); + ymax = Min(iy + 10, NUMSECTORS_Y); assert(ymin <= ymax); // Delete a block of sectors that we know is behind the camera if(TheCamera.GetForward().x > 0){ // looking east - xmax = max(ix - 2, 0); - xmin = max(ix - 10, 0); + xmax = Max(ix - 2, 0); + xmin = Max(ix - 10, 0); inc = 1; }else{ // looking west - xmax = min(ix + 2, NUMSECTORS_X); - xmin = min(ix + 10, NUMSECTORS_X); + xmax = Min(ix + 2, NUMSECTORS_X); + xmin = Min(ix + 10, NUMSECTORS_X); inc = -1; } for(y = ymin; y <= ymax; y++){ @@ -2189,13 +2189,13 @@ CStreaming::DeleteRwObjectsBehindCamera(int32 mem) // Now a block that intersects with the camera's frustum if(TheCamera.GetForward().x > 0){ // looking east - xmax = max(ix + 10, 0); - xmin = max(ix - 2, 0); + xmax = Max(ix + 10, 0); + xmin = Max(ix - 2, 0); inc = 1; }else{ // looking west - xmax = min(ix - 10, NUMSECTORS_X); - xmin = min(ix + 2, NUMSECTORS_X); + xmax = Min(ix - 10, NUMSECTORS_X); + xmin = Min(ix + 2, NUMSECTORS_X); inc = -1; } for(y = ymin; y <= ymax; y++){ @@ -2224,20 +2224,20 @@ CStreaming::DeleteRwObjectsBehindCamera(int32 mem) }else{ // looking north/south - xmin = max(ix - 10, 0); - xmax = min(ix + 10, NUMSECTORS_X); + xmin = Max(ix - 10, 0); + xmax = Min(ix + 10, NUMSECTORS_X); assert(xmin <= xmax); // Delete a block of sectors that we know is behind the camera if(TheCamera.GetForward().y > 0){ // looking north - ymax = max(iy - 2, 0); - ymin = max(iy - 10, 0); + ymax = Max(iy - 2, 0); + ymin = Max(iy - 10, 0); inc = 1; }else{ // looking south - ymax = min(iy + 2, NUMSECTORS_Y); - ymin = min(iy + 10, NUMSECTORS_Y); + ymax = Min(iy + 2, NUMSECTORS_Y); + ymin = Min(iy + 10, NUMSECTORS_Y); inc = -1; } for(x = xmin; x <= xmax; x++){ @@ -2253,13 +2253,13 @@ CStreaming::DeleteRwObjectsBehindCamera(int32 mem) // Now a block that intersects with the camera's frustum if(TheCamera.GetForward().y > 0){ // looking north - ymax = max(iy + 10, 0); - ymin = max(iy - 2, 0); + ymax = Max(iy + 10, 0); + ymin = Max(iy - 2, 0); inc = 1; }else{ // looking south - ymax = min(iy - 10, NUMSECTORS_Y); - ymin = min(iy + 2, NUMSECTORS_Y); + ymax = Min(iy - 10, NUMSECTORS_Y); + ymin = Min(iy + 2, NUMSECTORS_Y); inc = -1; } for(x = xmin; x <= xmax; x++){ diff --git a/src/core/Wanted.cpp b/src/core/Wanted.cpp index 26b115e3..daed9155 100644 --- a/src/core/Wanted.cpp +++ b/src/core/Wanted.cpp @@ -209,7 +209,7 @@ CWanted::ReportCrimeNow(eCrimeType type, const CVector &coors, bool policeDoesnt else sensitivity = m_fCrimeSensitivity; - wantedLevelDrop = min(CCullZones::GetWantedLevelDrop(), 100); + wantedLevelDrop = Min(CCullZones::GetWantedLevelDrop(), 100); chaos = (1.0f - wantedLevelDrop/100.0f) * sensitivity; if (policeDoesntCare) diff --git a/src/core/common.h b/src/core/common.h index 36f67bfa..73e57c21 100644 --- a/src/core/common.h +++ b/src/core/common.h @@ -177,10 +177,10 @@ void re3_assert(const char *expr, const char *filename, unsigned int lineno, con #define DEBUGBREAK() __debugbreak(); -#define debug(f, ...) re3_debug("[DBG]: " f, __VA_ARGS__) -#define DEV(f, ...) re3_debug("[DEV]: " f, __VA_ARGS__) -#define TRACE(f, ...) re3_trace(__FILE__, __LINE__, __FUNCTION__, f, __VA_ARGS__) -#define Error(f, ...) re3_debug("[ERROR]: " f, __VA_ARGS__) +#define debug(f, ...) re3_debug("[DBG]: " f, ## __VA_ARGS__) +#define DEV(f, ...) re3_debug("[DEV]: " f, ## __VA_ARGS__) +#define TRACE(f, ...) re3_trace(__FILE__, __LINE__, __FUNCTION__, f, ## __VA_ARGS__) +#define Error(f, ...) re3_debug("[ERROR]: " f, ## __VA_ARGS__) #define assert(_Expression) (void)( (!!(_Expression)) || (re3_assert(#_Expression, __FILE__, __LINE__, __FUNCTION__), 0) ) #define ASSERT assert @@ -200,7 +200,6 @@ void re3_assert(const char *expr, const char *filename, unsigned int lineno, con #define ABS(a) (((a) < 0) ? (-(a)) : (a)) #define norm(value, min, max) (((value) < (min)) ? 0 : (((value) > (max)) ? 1 : (((value) - (min)) / ((max) - (min))))) - #define STRINGIFY(x) #x #define STR(x) STRINGIFY(x) #define CONCAT_(x,y) x##y diff --git a/src/core/main.cpp b/src/core/main.cpp index 50494ef3..a82a2ab8 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -453,7 +453,7 @@ DoFade(void) CRGBA fadeColor; CRect rect; int fadeValue = CDraw::FadeValue; - float brightness = min(CMenuManager::m_PrefsBrightness, 256); + float brightness = Min(CMenuManager::m_PrefsBrightness, 256); if(brightness <= 50) brightness = 50; if(FrontEndMenuManager.m_bMenuActive) diff --git a/src/core/re3.cpp b/src/core/re3.cpp index ad3838bd..0ce5ee3c 100644 --- a/src/core/re3.cpp +++ b/src/core/re3.cpp @@ -374,6 +374,32 @@ delayedPatches10(int a, int b) } */ +<<<<<<< HEAD +======= +void __declspec(naked) HeadlightsFix() +{ + static const float fMinusOne = -1.0f; + _asm + { + fld [esp+708h-690h] + fcomp fMinusOne + fnstsw ax + and ah, 5 + cmp ah, 1 + jnz HeadlightsFix_DontLimit + fld fMinusOne + fstp [esp+708h-690h] + +HeadlightsFix_DontLimit: + fld [esp+708h-690h] + fabs + fld st + push 0x5382F2 + retn + } +} + +>>>>>>> More audio ped const int re3_buffsize = 1024; static char re3_buff[re3_buffsize]; diff --git a/src/core/re3.cpp.autosave b/src/core/re3.cpp.autosave new file mode 100644 index 00000000..ad3838bd --- /dev/null +++ b/src/core/re3.cpp.autosave @@ -0,0 +1,483 @@ +#include <direct.h> +#include <csignal> +#include <windows.h> +#include "common.h" +#include "patcher.h" +#include "Renderer.h" +#include "Credits.h" +#include "Camera.h" +#include "Weather.h" +#include "Clock.h" +#include "World.h" +#include "Vehicle.h" +#include "ModelIndices.h" +#include "Streaming.h" +#include "PathFind.h" +#include "Boat.h" +#include "Heli.h" +#include "Automobile.h" +#include "Ped.h" +#include "debugmenu_public.h" + +#include <vector> +#include <list> + +std::vector<int32> usedAddresses; + +void **rwengine = *(void***)0x5A10E1; + +DebugMenuAPI gDebugMenuAPI; + +WRAPPER void *gtanew(uint32 sz) { EAXJMP(0x5A0690); } +WRAPPER void gtadelete(void *p) { EAXJMP(0x5A07E0); } + +// overload our own new/delete with GTA's functions +void *operator new(size_t sz) { return gtanew(sz); } +void operator delete(void *ptr) noexcept { gtadelete(ptr); } + +#ifdef USE_PS2_RAND +unsigned __int64 myrand_seed = 1; +#else +unsigned long int myrand_seed = 1; +#endif + +int +myrand(void) +{ +#ifdef USE_PS2_RAND + // Use our own implementation of rand, stolen from PS2 + myrand_seed = 0x5851F42D4C957F2D * myrand_seed + 1; + return ((myrand_seed >> 32) & 0x7FFFFFFF); +#else + // or original codewarrior rand + myrand_seed = myrand_seed * 1103515245 + 12345; + return((myrand_seed >> 16) & 0x7FFF); +#endif +} + +void +mysrand(unsigned int seed) +{ + myrand_seed = seed; +} + +void (*DebugMenuProcess)(void); +void (*DebugMenuRender)(void); +static void stub(void) { } + +void +DebugMenuInit(void) +{ + if(DebugMenuLoad()){ + DebugMenuProcess = (void(*)(void))GetProcAddress(gDebugMenuAPI.module, "DebugMenuProcess"); + DebugMenuRender = (void(*)(void))GetProcAddress(gDebugMenuAPI.module, "DebugMenuRender"); + } + if(DebugMenuProcess == nil || DebugMenuRender == nil){ + DebugMenuProcess = stub; + DebugMenuRender = stub; + } + +} + +void WeaponCheat(); +void HealthCheat(); +void TankCheat(); +void BlowUpCarsCheat(); +void ChangePlayerCheat(); +void MayhemCheat(); +void EverybodyAttacksPlayerCheat(); +void WeaponsForAllCheat(); +void FastTimeCheat(); +void SlowTimeCheat(); +void MoneyCheat(); +void ArmourCheat(); +void WantedLevelUpCheat(); +void WantedLevelDownCheat(); +void SunnyWeatherCheat(); +void CloudyWeatherCheat(); +void RainyWeatherCheat(); +void FoggyWeatherCheat(); +void FastWeatherCheat(); +void OnlyRenderWheelsCheat(); +void ChittyChittyBangBangCheat(); +void StrongGripCheat(); +void NastyLimbsCheat(); + +DebugMenuEntry *carCol1; +DebugMenuEntry *carCol2; + +void +SpawnCar(int id) +{ + CVector playerpos; + CStreaming::RequestModel(id, 0); + CStreaming::LoadAllRequestedModels(false); + if(CStreaming::HasModelLoaded(id)){ + playerpos = FindPlayerCoors(); + int node = ThePaths.FindNodeClosestToCoors(playerpos, 0, 100.0f, false, false); + if(node < 0) + return; + + CVehicle *v; + if(CModelInfo::IsBoatModel(id)) + return; + else + v = new CAutomobile(id, RANDOM_VEHICLE); + + v->bHasBeenOwnedByPlayer = true; + if(carCol1) + DebugMenuEntrySetAddress(carCol1, &v->m_currentColour1); + if(carCol2) + DebugMenuEntrySetAddress(carCol2, &v->m_currentColour2); + + v->GetPosition() = ThePaths.m_pathNodes[node].pos; + v->GetPosition().z += 4.0f; + v->SetOrientation(0.0f, 0.0f, 3.49f); + v->m_status = STATUS_ABANDONED; + v->m_nDoorLock = CARLOCK_UNLOCKED; + CWorld::Add(v); + } +} + +static void +LetThemFollowYou(void) { + CPed* player = (CPed*) FindPlayerPed(); + for (int i = 0; i < player->m_numNearPeds; i++) { + + CPed* nearPed = player->m_nearPeds[i]; + if (nearPed && !nearPed->IsPlayer()) { + nearPed->SetObjective(OBJECTIVE_FOLLOW_PED_IN_FORMATION, (void*)player); + nearPed->m_pedFormation = rand() & 7; + nearPed->bScriptObjectiveCompleted = false; + } + } +} + +static void +FixCar(void) +{ + CVehicle *veh = FindPlayerVehicle(); + if(veh == nil) + return; + veh->m_fHealth = 1000.0f; + if(!veh->IsCar()) + return; + ((CAutomobile*)veh)->Damage.SetEngineStatus(0); + ((CAutomobile*)veh)->Fix(); +} + +static int engineStatus; +static void +SetEngineStatus(void) +{ + CVehicle *veh = FindPlayerVehicle(); + if(veh == nil) + return; + if(!veh->IsCar()) + return; + ((CAutomobile*)veh)->Damage.SetEngineStatus(engineStatus); +} + +static void +ToggleComedy(void) +{ + CVehicle *veh = FindPlayerVehicle(); + if(veh == nil) + return; + veh->bComedyControls = !veh->bComedyControls; +} + +static void +PlaceOnRoad(void) +{ + CVehicle *veh = FindPlayerVehicle(); + if(veh == nil) + return; + + if(veh->IsCar()) + ((CAutomobile*)veh)->PlaceOnRoadProperly(); +} + +static const char *carnames[] = { + "landstal", "idaho", "stinger", "linerun", "peren", "sentinel", "patriot", "firetruk", "trash", "stretch", "manana", "infernus", "blista", "pony", + "mule", "cheetah", "ambulan", "fbicar", "moonbeam", "esperant", "taxi", "kuruma", "bobcat", "mrwhoop", "bfinject", "corpse", "police", "enforcer", + "securica", "banshee", "predator", "bus", "rhino", "barracks", "train", "chopper", "dodo", "coach", "cabbie", "stallion", "rumpo", "rcbandit", + "bellyup", "mrwongs", "mafia", "yardie", "yakuza", "diablos", "columb", "hoods", "airtrain", "deaddodo", "speeder", "reefer", "panlant", "flatbed", + "yankee", "escape", "borgnine", "toyz", "ghost", +}; + +static std::list<CTweakVar *> TweakVarsList; +static bool bAddTweakVarsNow = false; +static const char *pTweakVarsDefaultPath = NULL; + +void CTweakVars::Add(CTweakVar *var) +{ + TweakVarsList.push_back(var); + + if ( bAddTweakVarsNow ) + var->AddDBG(pTweakVarsDefaultPath); +} + +void CTweakVars::AddDBG(const char *path) +{ + pTweakVarsDefaultPath = path; + + for(auto i = TweakVarsList.begin(); i != TweakVarsList.end(); ++i) + (*i)->AddDBG(pTweakVarsDefaultPath); + + bAddTweakVarsNow = true; +} + +void CTweakSwitch::AddDBG(const char *path) +{ + DebugMenuEntry *e = DebugMenuAddVar(m_pPath == NULL ? path : m_pPath, m_pVarName, (int32_t *)m_pIntVar, m_pFunc, 1, m_nMin, m_nMax, m_aStr); + DebugMenuEntrySetWrap(e, true); +} + +void CTweakFunc::AddDBG (const char *path) { DebugMenuAddCmd (m_pPath == NULL ? path : m_pPath, m_pVarName, m_pFunc); } +void CTweakBool::AddDBG (const char *path) { DebugMenuAddVarBool8(m_pPath == NULL ? path : m_pPath, m_pVarName, (int8_t *)m_pBoolVar, NULL); } +void CTweakInt8::AddDBG (const char *path) { DebugMenuAddVar (m_pPath == NULL ? path : m_pPath, m_pVarName, (int8_t *)m_pIntVar, NULL, m_nStep, m_nLoawerBound, m_nUpperBound, NULL); } +void CTweakUInt8::AddDBG (const char *path) { DebugMenuAddVar (m_pPath == NULL ? path : m_pPath, m_pVarName, (uint8_t *)m_pIntVar, NULL, m_nStep, m_nLoawerBound, m_nUpperBound, NULL); } +void CTweakInt16::AddDBG (const char *path) { DebugMenuAddVar (m_pPath == NULL ? path : m_pPath, m_pVarName, (int16_t *)m_pIntVar, NULL, m_nStep, m_nLoawerBound, m_nUpperBound, NULL); } +void CTweakUInt16::AddDBG(const char *path) { DebugMenuAddVar (m_pPath == NULL ? path : m_pPath, m_pVarName, (uint16_t *)m_pIntVar, NULL, m_nStep, m_nLoawerBound, m_nUpperBound, NULL); } +void CTweakInt32::AddDBG (const char *path) { DebugMenuAddVar (m_pPath == NULL ? path : m_pPath, m_pVarName, (int32_t *)m_pIntVar, NULL, m_nStep, m_nLoawerBound, m_nUpperBound, NULL); } +void CTweakUInt32::AddDBG(const char *path) { DebugMenuAddVar (m_pPath == NULL ? path : m_pPath, m_pVarName, (uint32_t *)m_pIntVar, NULL, m_nStep, m_nLoawerBound, m_nUpperBound, NULL); } +void CTweakFloat::AddDBG (const char *path) { DebugMenuAddVar (m_pPath == NULL ? path : m_pPath, m_pVarName, (float *)m_pIntVar, NULL, m_nStep, m_nLoawerBound, m_nUpperBound); } + +/* +static const char *wt[] = { + "Sunny", "Cloudy", "Rainy", "Foggy" + }; + +SETTWEAKPATH("TEST"); +TWEAKSWITCH(CWeather::NewWeatherType, 0, 3, wt, NULL); +*/ + +void +DebugMenuPopulate(void) +{ + if(DebugMenuLoad()){ + static const char *weathers[] = { + "Sunny", "Cloudy", "Rainy", "Foggy" + }; + DebugMenuEntry *e; + e = DebugMenuAddVar("Time & Weather", "Current Hour", &CClock::GetHoursRef(), nil, 1, 0, 23, nil); + DebugMenuEntrySetWrap(e, true); + e = DebugMenuAddVar("Time & Weather", "Current Minute", &CClock::GetMinutesRef(), + [](){ CWeather::InterpolationValue = CClock::GetMinutes()/60.0f; }, 1, 0, 59, nil); + DebugMenuEntrySetWrap(e, true); + e = DebugMenuAddVar("Time & Weather", "Old Weather", (int16*)&CWeather::OldWeatherType, nil, 1, 0, 3, weathers); + DebugMenuEntrySetWrap(e, true); + e = DebugMenuAddVar("Time & Weather", "New Weather", (int16*)&CWeather::NewWeatherType, nil, 1, 0, 3, weathers); + DebugMenuEntrySetWrap(e, true); + DebugMenuAddVar("Time & Weather", "Wind", (float*)&CWeather::Wind, nil, 0.1f, 0.0f, 1.0f); + DebugMenuAddVar("Time & Weather", "Time scale", (float*)0x8F2C20, nil, 0.1f, 0.0f, 10.0f); + + DebugMenuAddCmd("Cheats", "Weapons", WeaponCheat); + DebugMenuAddCmd("Cheats", "Money", MoneyCheat); + DebugMenuAddCmd("Cheats", "Health", HealthCheat); + DebugMenuAddCmd("Cheats", "Wanted level up", WantedLevelUpCheat); + DebugMenuAddCmd("Cheats", "Wanted level down", WantedLevelDownCheat); + DebugMenuAddCmd("Cheats", "Tank", TankCheat); + DebugMenuAddCmd("Cheats", "Blow up cars", BlowUpCarsCheat); + DebugMenuAddCmd("Cheats", "Change player", ChangePlayerCheat); + DebugMenuAddCmd("Cheats", "Mayhem", MayhemCheat); + DebugMenuAddCmd("Cheats", "Everybody attacks player", EverybodyAttacksPlayerCheat); + DebugMenuAddCmd("Cheats", "Weapons for all", WeaponsForAllCheat); + DebugMenuAddCmd("Cheats", "Fast time", FastTimeCheat); + DebugMenuAddCmd("Cheats", "Slow time", SlowTimeCheat); + DebugMenuAddCmd("Cheats", "Armour", ArmourCheat); + DebugMenuAddCmd("Cheats", "Sunny weather", SunnyWeatherCheat); + DebugMenuAddCmd("Cheats", "Cloudy weather", CloudyWeatherCheat); + DebugMenuAddCmd("Cheats", "Rainy weather", RainyWeatherCheat); + DebugMenuAddCmd("Cheats", "Foggy weather", FoggyWeatherCheat); + DebugMenuAddCmd("Cheats", "Fast weather", FastWeatherCheat); + DebugMenuAddCmd("Cheats", "Only render wheels", OnlyRenderWheelsCheat); + DebugMenuAddCmd("Cheats", "Chitty chitty bang bang", ChittyChittyBangBangCheat); + DebugMenuAddCmd("Cheats", "Strong grip", StrongGripCheat); + DebugMenuAddCmd("Cheats", "Nasty limbs", NastyLimbsCheat); + + static int spawnCarId = MI_LANDSTAL; + e = DebugMenuAddVar("Spawn", "Spawn Car ID", &spawnCarId, nil, 1, MI_LANDSTAL, MI_GHOST, carnames); + DebugMenuEntrySetWrap(e, true); + DebugMenuAddCmd("Spawn", "Spawn Car", [](){ + if(spawnCarId == MI_TRAIN || + spawnCarId == MI_CHOPPER || + spawnCarId == MI_AIRTRAIN || + spawnCarId == MI_DEADDODO || + spawnCarId == MI_ESCAPE) + return; + SpawnCar(spawnCarId); + }); + static uint8 dummy; + carCol1 = DebugMenuAddVar("Spawn", "First colour", &dummy, nil, 1, 0, 255, nil); + carCol2 = DebugMenuAddVar("Spawn", "Second colour", &dummy, nil, 1, 0, 255, nil); + DebugMenuAddCmd("Spawn", "Spawn Stinger", [](){ SpawnCar(MI_STINGER); }); + DebugMenuAddCmd("Spawn", "Spawn Infernus", [](){ SpawnCar(MI_INFERNUS); }); + DebugMenuAddCmd("Spawn", "Spawn Cheetah", [](){ SpawnCar(MI_CHEETAH); }); + DebugMenuAddCmd("Spawn", "Spawn Esperanto", [](){ SpawnCar(MI_ESPERANT); }); + DebugMenuAddCmd("Spawn", "Spawn Stallion", [](){ SpawnCar(MI_STALLION); }); + DebugMenuAddCmd("Spawn", "Spawn Kuruma", [](){ SpawnCar(MI_KURUMA); }); + DebugMenuAddCmd("Spawn", "Spawn Taxi", [](){ SpawnCar(MI_TAXI); }); + DebugMenuAddCmd("Spawn", "Spawn Police", [](){ SpawnCar(MI_POLICE); }); + DebugMenuAddCmd("Spawn", "Spawn Enforcer", [](){ SpawnCar(MI_ENFORCER); }); + DebugMenuAddCmd("Spawn", "Spawn Banshee", [](){ SpawnCar(MI_BANSHEE); }); + DebugMenuAddCmd("Spawn", "Spawn Yakuza", [](){ SpawnCar(MI_YAKUZA); }); + DebugMenuAddCmd("Spawn", "Spawn Yardie", [](){ SpawnCar(MI_YARDIE); }); + DebugMenuAddCmd("Spawn", "Spawn Dodo", [](){ SpawnCar(MI_DODO); }); + DebugMenuAddCmd("Spawn", "Spawn Rhino", [](){ SpawnCar(MI_RHINO); }); + DebugMenuAddCmd("Spawn", "Spawn Firetruck", [](){ SpawnCar(MI_FIRETRUCK); }); + + DebugMenuAddVar("Debug", "Engine Status", &engineStatus, nil, 1, 0, 226, nil); + DebugMenuAddCmd("Debug", "Set Engine Status", SetEngineStatus); + DebugMenuAddCmd("Debug", "Fix Car", FixCar); + DebugMenuAddCmd("Debug", "Toggle Comedy Controls", ToggleComedy); + DebugMenuAddCmd("Debug", "Place Car on Road", PlaceOnRoad); + + DebugMenuAddVarBool8("Debug", "Catalina Heli On", (int8*)&CHeli::CatalinaHeliOn, nil); + DebugMenuAddCmd("Debug", "Catalina Fly By", CHeli::StartCatalinaFlyBy); + DebugMenuAddCmd("Debug", "Catalina Take Off", CHeli::CatalinaTakeOff); + DebugMenuAddCmd("Debug", "Catalina Fly Away", CHeli::MakeCatalinaHeliFlyAway); + DebugMenuAddVarBool8("Debug", "Script Heli On", (int8*)0x95CD43, nil); + + DebugMenuAddVarBool8("Debug", "Show Ped Road Groups", (int8*)&gbShowPedRoadGroups, nil); + DebugMenuAddVarBool8("Debug", "Show Car Road Groups", (int8*)&gbShowCarRoadGroups, nil); + DebugMenuAddVarBool8("Debug", "Show Collision Lines", (int8*)&gbShowCollisionLines, nil); + DebugMenuAddVarBool8("Debug", "Show Collision Polys", (int8*)&gbShowCollisionPolys, nil); + DebugMenuAddVarBool8("Debug", "Don't render Buildings", (int8*)&gbDontRenderBuildings, nil); + DebugMenuAddVarBool8("Debug", "Don't render Big Buildings", (int8*)&gbDontRenderBigBuildings, nil); + DebugMenuAddVarBool8("Debug", "Don't render Peds", (int8*)&gbDontRenderPeds, nil); + DebugMenuAddVarBool8("Debug", "Don't render Vehicles", (int8*)&gbDontRenderVehicles, nil); + DebugMenuAddVarBool8("Debug", "Don't render Objects", (int8*)&gbDontRenderObjects, nil); + + DebugMenuAddCmd("Debug", "Make peds around you follow you", LetThemFollowYou); +#ifndef MASTER + DebugMenuAddVarBool8("Debug", "Toggle unused fight feature", (int8*)&CPed::bUnusedFightThingOnPlayer, nil); +#endif + + DebugMenuAddCmd("Debug", "Start Credits", CCredits::Start); + DebugMenuAddCmd("Debug", "Stop Credits", CCredits::Stop); + + CTweakVars::AddDBG("Debug"); + } +} + +/* +int (*RsEventHandler_orig)(int a, int b); +int +delayedPatches10(int a, int b) +{ + DebugMenuInit(); + DebugMenuPopulate(); + + return RsEventHandler_orig(a, b); +} +*/ + +const int re3_buffsize = 1024; +static char re3_buff[re3_buffsize]; + +void re3_assert(const char *expr, const char *filename, unsigned int lineno, const char *func) +{ + int nCode; + + strcpy_s(re3_buff, re3_buffsize, "Assertion failed!" ); + strcat_s(re3_buff, re3_buffsize, "\n" ); + + strcat_s(re3_buff, re3_buffsize, "File: "); + strcat_s(re3_buff, re3_buffsize, filename ); + strcat_s(re3_buff, re3_buffsize, "\n" ); + + strcat_s(re3_buff, re3_buffsize, "Line: " ); + _itoa_s( lineno, re3_buff + strlen(re3_buff), re3_buffsize - strlen(re3_buff), 10 ); + strcat_s(re3_buff, re3_buffsize, "\n"); + + strcat_s(re3_buff, re3_buffsize, "Function: "); + strcat_s(re3_buff, re3_buffsize, func ); + strcat_s(re3_buff, re3_buffsize, "\n" ); + + strcat_s(re3_buff, re3_buffsize, "Expression: "); + strcat_s(re3_buff, re3_buffsize, expr); + strcat_s(re3_buff, re3_buffsize, "\n"); + + strcat_s(re3_buff, re3_buffsize, "\n" ); + strcat_s(re3_buff, re3_buffsize, "(Press Retry to debug the application)"); + + + nCode = ::MessageBoxA(nil, re3_buff, "RE3 Assertion Failed!", + MB_ABORTRETRYIGNORE|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL); + + if (nCode == IDABORT) + { + raise(SIGABRT); + _exit(3); + } + + if (nCode == IDRETRY) + { + __debugbreak(); + return; + } + + if (nCode == IDIGNORE) + return; + + abort(); +} + +void re3_debug(char *format, ...) +{ + va_list va; + va_start(va, format); + vsprintf_s(re3_buff, re3_buffsize, format, va); + va_end(va); + + printf("%s", re3_buff); +} + +void re3_trace(const char *filename, unsigned int lineno, const char *func, char *format, ...) +{ + char buff[re3_buffsize *2]; + va_list va; + va_start(va, format); + vsprintf_s(re3_buff, re3_buffsize, format, va); + va_end(va); + + sprintf_s(buff, re3_buffsize * 2, "[%s.%s:%d]: %s", filename, func, lineno, re3_buff); + + OutputDebugStringA(buff); +} + +void +patch() +{ + StaticPatcher::Apply(); + +// Patch<float>(0x46BC61+6, 1.0f); // car distance + InjectHook(0x59E460, printf, PATCH_JUMP); + InjectHook(0x475E00, printf, PATCH_JUMP); // _Error + + +// InterceptCall(&open_script_orig, open_script, 0x438869); + +// InterceptCall(&RsEventHandler_orig, delayedPatches10, 0x58275E); +} + +BOOL WINAPI +DllMain(HINSTANCE hInst, DWORD reason, LPVOID) +{ + if(reason == DLL_PROCESS_ATTACH){ + + AllocConsole(); + freopen("CONIN$", "r", stdin); + freopen("CONOUT$", "w", stdout); + freopen("CONOUT$", "w", stderr); + + if (*(DWORD*)0x5C1E75 == 0xB85548EC) // 1.0 + patch(); + else + return FALSE; + } + + return TRUE; +} diff --git a/src/entities/Physical.cpp b/src/entities/Physical.cpp index fbd1322d..d59e6c59 100644 --- a/src/entities/Physical.cpp +++ b/src/entities/Physical.cpp @@ -457,7 +457,7 @@ CPhysical::ApplySpringCollision(float springConst, CVector &springDir, CVector & { float compression = 1.0f - springRatio; if(compression > 0.0f){ - float step = min(CTimer::GetTimeStep(), 3.0f); + float step = Min(CTimer::GetTimeStep(), 3.0f); float impulse = -GRAVITY*m_fMass*step * springConst * compression * bias*2.0f; ApplyMoveForce(springDir*impulse); ApplyTurnForce(springDir*impulse, point); @@ -471,12 +471,12 @@ CPhysical::ApplySpringDampening(float damping, CVector &springDir, CVector &poin { float speedA = DotProduct(speed, springDir); float speedB = DotProduct(GetSpeed(point), springDir); - float step = min(CTimer::GetTimeStep(), 3.0f); + float step = Min(CTimer::GetTimeStep(), 3.0f); float impulse = -damping * (speedA + speedB)/2.0f * m_fMass * step * 0.53f; // what is this? float a = m_fTurnMass / ((point.MagnitudeSqr() + 1.0f) * 2.0f * m_fMass); - a = min(a, 1.0f); + a = Min(a, 1.0f); float b = Abs(impulse / (speedB * m_fMass)); if(a < b) impulse *= a/b; @@ -646,7 +646,7 @@ CPhysical::ApplyCollision(CPhysical *B, CColPoint &colpoint, float &impulseA, fl // positive if B is moving towards A // not interested in how much B moves into A apparently? // only interested in cases where A collided into B - speedB = max(0.0f, DotProduct(B->m_vecMoveSpeed, colpoint.normal)); + speedB = Max(0.0f, DotProduct(B->m_vecMoveSpeed, colpoint.normal)); // A has moved into B if(speedA < speedB){ if(!A->bHasHitWall) @@ -1147,18 +1147,18 @@ CPhysical::ProcessShiftSectorList(CPtrList *lists) CVector dir = A->GetPosition() - B->GetPosition(); dir.Normalise(); if(dir.z < 0.0f && dir.z < A->GetForward().z && dir.z < A->GetRight().z) - dir.z = min(0.0f, min(A->GetForward().z, A->GetRight().z)); + dir.z = Min(0.0f, Min(A->GetForward().z, A->GetRight().z)); shift += dir * colpoints[mostColliding].depth * 0.5f; }else if(A->IsPed() && B->IsVehicle() && ((CVehicle*)B)->IsBoat()){ CVector dir = colpoints[mostColliding].normal; - float f = min(Abs(dir.z), 0.9f); + float f = Min(Abs(dir.z), 0.9f); dir.z = 0.0f; dir.Normalise(); shift += dir * colpoints[mostColliding].depth / (1.0f - f); boat = B; }else if(B->IsPed() && A->IsVehicle() && ((CVehicle*)A)->IsBoat()){ CVector dir = colpoints[mostColliding].normal * -1.0f; - float f = min(Abs(dir.z), 0.9f); + float f = Min(Abs(dir.z), 0.9f); dir.z = 0.0f; dir.Normalise(); B->GetPosition() += dir * colpoints[mostColliding].depth / (1.0f - f); @@ -1246,7 +1246,7 @@ collision: float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr(); float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr(); - DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, max(turnSpeedDiff, moveSpeedDiff)); + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); } }else if(A->bHasContacted){ CVector savedMoveFriction = A->m_vecMoveFriction; @@ -1268,7 +1268,7 @@ collision: float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr(); float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr(); - DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, max(turnSpeedDiff, moveSpeedDiff)); + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){ A->bHasContacted = true; @@ -1301,7 +1301,7 @@ collision: float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr(); float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr(); - DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, max(turnSpeedDiff, moveSpeedDiff)); + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){ A->bHasContacted = true; @@ -1328,7 +1328,7 @@ collision: float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr(); float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr(); - DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, max(turnSpeedDiff, moveSpeedDiff)); + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){ A->bHasContacted = true; @@ -1506,7 +1506,7 @@ CPhysical::ProcessCollisionSectorList(CPtrList *lists) float turnSpeedDiff = A->m_vecTurnSpeed.MagnitudeSqr(); float moveSpeedDiff = A->m_vecMoveSpeed.MagnitudeSqr(); - DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, imp, max(turnSpeedDiff, moveSpeedDiff)); + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, imp, Max(turnSpeedDiff, moveSpeedDiff)); } }else{ for(i = 0; i < numCollisions; i++){ @@ -1527,7 +1527,7 @@ CPhysical::ProcessCollisionSectorList(CPtrList *lists) float turnSpeedDiff = A->m_vecTurnSpeed.MagnitudeSqr(); float moveSpeedDiff = A->m_vecMoveSpeed.MagnitudeSqr(); - DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, imp, max(turnSpeedDiff, moveSpeedDiff)); + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, imp, Max(turnSpeedDiff, moveSpeedDiff)); float adhesion = CSurfaceTable::GetAdhesiveLimit(aColPoints[i]) / numCollisions; @@ -1545,7 +1545,7 @@ CPhysical::ProcessCollisionSectorList(CPtrList *lists) else if(A->GetUp().z > 0.3f) adhesion = 0.0f; else - adhesion *= min(5.0f, 0.03f*impulseA + 1.0f); + adhesion *= Min(5.0f, 0.03f*impulseA + 1.0f); } if(A->ApplyFriction(adhesion, aColPoints[i])) @@ -1594,7 +1594,7 @@ CPhysical::ProcessCollisionSectorList(CPtrList *lists) float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr(); float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr(); - DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, max(turnSpeedDiff, moveSpeedDiff)); + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); } }else if(A->bHasContacted){ CVector savedMoveFriction = A->m_vecMoveFriction; @@ -1619,7 +1619,7 @@ CPhysical::ProcessCollisionSectorList(CPtrList *lists) float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr(); float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr(); - DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, max(turnSpeedDiff, moveSpeedDiff)); + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){ A->bHasContacted = true; @@ -1655,7 +1655,7 @@ CPhysical::ProcessCollisionSectorList(CPtrList *lists) float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr(); float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr(); - DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, max(turnSpeedDiff, moveSpeedDiff)); + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){ A->bHasContacted = true; @@ -1685,7 +1685,7 @@ CPhysical::ProcessCollisionSectorList(CPtrList *lists) float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr(); float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr(); - DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, max(turnSpeedDiff, moveSpeedDiff)); + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){ A->bHasContacted = true; @@ -1831,7 +1831,7 @@ CPhysical::ProcessCollision(void) if(IsPed() && (distSq >= sq(0.2f) || ped->IsPlayer())){ if(ped->IsPlayer()) - n = max(NUMSTEPS(0.2f), 2.0); + n = Max(NUMSTEPS(0.2f), 2.0); else n = NUMSTEPS(0.3f); step = savedTimeStep / n; @@ -1852,7 +1852,7 @@ CPhysical::ProcessCollision(void) speedDown = Multiply3x3(GetMatrix(), speedDown); speedUp = GetSpeed(speedUp); speedDown = GetSpeed(speedDown); - distSq = max(speedUp.MagnitudeSqr(), speedDown.MagnitudeSqr()) * sq(CTimer::GetTimeStep()); + distSq = Max(speedUp.MagnitudeSqr(), speedDown.MagnitudeSqr()) * sq(CTimer::GetTimeStep()); if(distSq >= sq(0.3f)){ n = NUMSTEPS(0.3f); step = savedTimeStep / n; diff --git a/src/peds/Ped.cpp b/src/peds/Ped.cpp index 53d94d86..59395d94 100644 --- a/src/peds/Ped.cpp +++ b/src/peds/Ped.cpp @@ -1792,7 +1792,7 @@ CPed::LineUpPedWithCar(PedLineUpPhase phase) neededPos.z = autoZPos.z; m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); } else if (neededPos.z <= currentZ && m_pVehicleAnim && vehAnim != ANIM_VAN_CLOSE_L && vehAnim != ANIM_VAN_CLOSE) { - adjustedTimeStep = min(m_pVehicleAnim->timeStep, 0.1f); + adjustedTimeStep = Min(m_pVehicleAnim->timeStep, 0.1f); // Smoothly change ped position neededPos.z = currentZ - (currentZ - neededPos.z) / (m_pVehicleAnim->GetTimeLeft() / adjustedTimeStep); @@ -1807,12 +1807,12 @@ CPed::LineUpPedWithCar(PedLineUpPhase phase) if (m_pVehicleAnim && (vehAnim == ANIM_CAR_GETIN_RHS || vehAnim == ANIM_CAR_GETIN_LOW_RHS || vehAnim == ANIM_CAR_GETIN_LHS || vehAnim == ANIM_CAR_GETIN_LOW_LHS || vehAnim == ANIM_CAR_QJACK || vehAnim == ANIM_VAN_GETIN_L || vehAnim == ANIM_VAN_GETIN)) { - adjustedTimeStep = min(m_pVehicleAnim->timeStep, 0.1f); + adjustedTimeStep = Min(m_pVehicleAnim->timeStep, 0.1f); // Smoothly change ped position neededPos.z = (neededPos.z - currentZ) / (m_pVehicleAnim->GetTimeLeft() / adjustedTimeStep) + currentZ; } else if (m_nPedState == PED_ENTER_CAR || m_nPedState == PED_CARJACK) { - neededPos.z = max(currentZ, autoZPos.z); + neededPos.z = Max(currentZ, autoZPos.z); } } } @@ -3172,7 +3172,7 @@ CPed::CheckIfInTheAir(void) CEntity *foundEntity; float startZ = pos.z - 1.54f; - bool foundGround = CWorld::ProcessVerticalLine(pos, startZ, foundColPoint, foundEntity, true, true, false, true, false, false, false); + bool foundGround = CWorld::ProcessVerticalLine(pos, startZ, foundColPoint, foundEntity, true, true, false, true, false, false, nil); if (!foundGround && m_nPedState != PED_JUMP) { pos.z -= 1.04f; @@ -4771,12 +4771,12 @@ CPed::FightStrike(CVector &touchedNodePos) float moveMult; if (m_lastFightMove == FIGHTMOVE_GROUNDKICK) { - moveMult = min(damageMult * 0.6f, 4.0f); + moveMult = Min(damageMult * 0.6f, 4.0f); } else { if (nearPed->m_nPedState != PED_DIE || damageMult >= 20) { moveMult = damageMult; } else { - moveMult = min(damageMult * 2.0f, 14.0f); + moveMult = Min(damageMult * 2.0f, 14.0f); } } diff --git a/src/peds/Ped.h b/src/peds/Ped.h index 5922e0e7..ff581150 100644 --- a/src/peds/Ped.h +++ b/src/peds/Ped.h @@ -718,6 +718,15 @@ public: #endif }; +class cPedParams +{ +public: + char m_bDistanceCalculated; + char gap_1[3]; + float m_fDistance; + CPed *m_pPed; +}; + void FinishFuckUCB(CAnimBlendAssociation *assoc, void *arg); static_assert(offsetof(CPed, m_nPedState) == 0x224, "CPed: error"); diff --git a/src/peds/PedPlacement.cpp b/src/peds/PedPlacement.cpp index f292f4fa..d7b7ec75 100644 --- a/src/peds/PedPlacement.cpp +++ b/src/peds/PedPlacement.cpp @@ -29,7 +29,7 @@ CPedPlacement::FindZCoorForPed(CVector* pos) if (CWorld::ProcessVerticalLine(vec, startZ, foundCol, foundEnt, true, false, false, false, true, false, nil)) foundColZ2 = foundCol.point.z; - zForPed = max(foundColZ, foundColZ2); + zForPed = Max(foundColZ, foundColZ2); if (zForPed > -99.0f) pos->z = 1.04f + zForPed; @@ -38,7 +38,7 @@ CPedPlacement::FindZCoorForPed(CVector* pos) CEntity* CPedPlacement::IsPositionClearOfCars(CVector* pos) { - return CWorld::TestSphereAgainstWorld(*pos, 0.25f, false, true, true, false, false, false, false); + return CWorld::TestSphereAgainstWorld(*pos, 0.25f, nil, true, true, false, false, false, false); } STARTPATCHES diff --git a/src/peds/PlayerPed.cpp b/src/peds/PlayerPed.cpp index 5ae8c4be..1dcbdf46 100644 --- a/src/peds/PlayerPed.cpp +++ b/src/peds/PlayerPed.cpp @@ -97,8 +97,16 @@ CPlayerPed::AnnoyPlayerPed(bool annoyedByPassingEntity) class CPlayerPed_ : public CPlayerPed { public: +<<<<<<< HEAD +<<<<<<< HEAD CPlayerPed* ctor(void) { return ::new (this) CPlayerPed(); } void dtor(void) { CPlayerPed::~CPlayerPed(); } +======= + void dtor(void) { this->~CPlayerPed(); } +>>>>>>> More audio ped +======= + void dtor(void) { CPlayerPed::~CPlayerPed(); } +>>>>>>> fix }; STARTPATCHES diff --git a/src/peds/PlayerPed.cpp.autosave b/src/peds/PlayerPed.cpp.autosave new file mode 100644 index 00000000..1bf1c7fc --- /dev/null +++ b/src/peds/PlayerPed.cpp.autosave @@ -0,0 +1,110 @@ +#include "common.h" +#include "patcher.h" +#include "PlayerPed.h" +#include "Camera.h" +#include "WeaponEffects.h" +#include "ModelIndices.h" + +CPlayerPed::~CPlayerPed() +{ + delete m_pWanted; +} + +WRAPPER void CPlayerPed::ReApplyMoveAnims(void) { EAXJMP(0x4F07C0); } +WRAPPER void CPlayerPed::SetupPlayerPed(int32) { EAXJMP(0x4EFB60); } +WRAPPER void CPlayerPed::DeactivatePlayerPed(int32) { EAXJMP(0x4EFC00); } +WRAPPER void CPlayerPed::ReactivatePlayerPed(int32) { EAXJMP(0x4EFC20); } +WRAPPER void CPlayerPed::KeepAreaAroundPlayerClear(void) { EAXJMP(0x4F3460); } +WRAPPER void CPlayerPed::MakeChangesForNewWeapon(int8) { EAXJMP(0x4F2560); } +WRAPPER void CPlayerPed::SetInitialState(void) { EAXJMP(0x4EFC40); } +WRAPPER void CPlayerPed::SetMoveAnim(void) { EAXJMP(0x4F3760); } +WRAPPER void CPlayerPed::ProcessControl(void) { EAXJMP(0x4EFD90); } + +CPlayerPed::CPlayerPed(void) : CPed(PEDTYPE_PLAYER1) +{ + m_fMoveSpeed = 0.0f; + SetModelIndex(MI_PLAYER); + SetInitialState(); + + m_pWanted = new CWanted(); + m_pWanted->Initialise(); + m_pArrestingCop = nil; + m_currentWeapon = WEAPONTYPE_UNARMED; + m_nSelectedWepSlot = 0; + m_nSpeedTimer = 0; + m_bSpeedTimerFlag = 0; + m_pPointGunAt = nil; + m_nPedState = PED_IDLE; + m_fMaxStamina = 150.0f; + m_fCurrentStamina = m_fMaxStamina; + m_fStaminaProgress = 0.0f; + m_bShouldEvade = 0; + field_1367 = 0; + m_nShotDelay = 0; + field_1376 = 0.0f; + field_1380 = 0; + m_bHasLockOnTarget = false; + m_bCanBeDamaged = true; + m_fWalkAngle = 0.0f; + m_fFPSMoveHeading = 0.0f; + m_nTargettableObjects[0] = m_nTargettableObjects[1] = m_nTargettableObjects[2] = m_nTargettableObjects[3] = -1; + field_1413 = 0; + for (int i = 0; i < 6; i++) { + m_vecSafePos[i] = CVector(0.0f, 0.0f, 0.0f); + field_1488[i] = 0; + } +} + +void CPlayerPed::ClearWeaponTarget() +{ + if (m_nPedType == PEDTYPE_PLAYER1) { + m_pPointGunAt = nil; + TheCamera.ClearPlayerWeaponMode(); + CWeaponEffects::ClearCrosshair(); + } + ClearPointGunAt(); +} + +void +CPlayerPed::SetWantedLevel(int32 level) +{ + m_pWanted->SetWantedLevel(level); +} + +void +CPlayerPed::SetWantedLevelNoDrop(int32 level) +{ + m_pWanted->SetWantedLevelNoDrop(level); +} + +// I don't know the actual purpose of parameter +void +CPlayerPed::AnnoyPlayerPed(bool annoyedByPassingEntity) +{ + if (m_pedStats->m_temper < 52) { + m_pedStats->m_temper++; + } else { + if (annoyedByPassingEntity) { + if (m_pedStats->m_temper < 55) { + m_pedStats->m_temper++; + } else { + m_pedStats->m_temper = 46; + } + } + } +} + +class CPlayerPed_ : public CPlayerPed +{ +public: + + CPlayerPed* ctor(void) { return ::new (this) CPlayerPed(); } + void dtor(void) { CPlayerPed::~CPlayerPed(); } +}; + +STARTPATCHES + InjectHook(0x4EF7E0, &CPlayerPed_::ctor, PATCH_JUMP); + InjectHook(0x4EFB30, &CPlayerPed_::dtor, PATCH_JUMP); + InjectHook(0x4F28A0, &CPlayerPed::ClearWeaponTarget, PATCH_JUMP); + InjectHook(0x4F3700, &CPlayerPed::AnnoyPlayerPed, PATCH_JUMP); +ENDPATCHES diff --git a/src/render/Clouds.cpp b/src/render/Clouds.cpp index 2884894c..a461301c 100644 --- a/src/render/Clouds.cpp +++ b/src/render/Clouds.cpp @@ -388,7 +388,7 @@ CClouds::RenderBackground(int16 topred, int16 topgreen, int16 topblue, ms_colourBottom.b = topblue; ms_colourBottom.a = alpha; - botpos = min(SCREEN_HEIGHT, topedge); + botpos = Min(SCREEN_HEIGHT, topedge); CSprite2d::DrawRect(CRect(0, 0, SCREEN_WIDTH, botpos), ms_colourBottom, ms_colourBottom, ms_colourTop, ms_colourTop); } @@ -415,18 +415,18 @@ CClouds::RenderHorizon(void) if(ms_horizonZ > SCREEN_HEIGHT) return; - float z1 = min(ms_horizonZ + SMALLSTRIPHEIGHT, SCREEN_HEIGHT); + float z1 = Min(ms_horizonZ + SMALLSTRIPHEIGHT, SCREEN_HEIGHT); CSprite2d::DrawRectXLU(CRect(0, ms_horizonZ, SCREEN_WIDTH, z1), ms_colourBottom, ms_colourBottom, ms_colourTop, ms_colourTop); // This is just weird float a = SCREEN_HEIGHT/400.0f * HORIZSTRIPHEIGHT + - SCREEN_HEIGHT/300.0f * max(TheCamera.GetPosition().z, 0.0f); + SCREEN_HEIGHT/300.0f * Max(TheCamera.GetPosition().z, 0.0f); float b = TheCamera.GetUp().z < 0.0f ? SCREEN_HEIGHT : SCREEN_HEIGHT * Abs(TheCamera.GetRight().z); float z2 = z1 + (a + b)*TheCamera.LODDistMultiplier; - z2 = min(z2, SCREEN_HEIGHT); + z2 = Min(z2, SCREEN_HEIGHT); CSprite2d::DrawRect(CRect(0, z1, SCREEN_WIDTH, z2), ms_colourBottom, ms_colourBottom, ms_colourTop, ms_colourTop); } diff --git a/src/render/Coronas.cpp b/src/render/Coronas.cpp index 1a6cfea3..9881e764 100644 --- a/src/render/Coronas.cpp +++ b/src/render/Coronas.cpp @@ -109,7 +109,7 @@ CCoronas::Update(void) int i; static int LastCamLook = 0; - LightsMult = min(LightsMult + 0.03f * CTimer::GetTimeStep(), 1.0f); + LightsMult = Min(LightsMult + 0.03f * CTimer::GetTimeStep(), 1.0f); int CamLook = 0; if(TheCamera.Cams[TheCamera.ActiveCam].LookingLeft) CamLook |= 1; @@ -121,7 +121,7 @@ CCoronas::Update(void) if(LastCamLook != CamLook) bChangeBrightnessImmediately = 3; else - bChangeBrightnessImmediately = max(bChangeBrightnessImmediately-1, 0); + bChangeBrightnessImmediately = Max(bChangeBrightnessImmediately-1, 0); LastCamLook = CamLook; for(i = 0; i < NUMCORONAS; i++) @@ -309,7 +309,7 @@ CCoronas::Render(void) // render corona itself if(aCoronas[i].texture){ - float fogscale = CWeather::Foggyness*min(spriteCoors.z, 40.0f)/40.0f + 1.0f; + float fogscale = CWeather::Foggyness*Min(spriteCoors.z, 40.0f)/40.0f + 1.0f; if(CCoronas::aCoronas[i].id == SUN_CORE) spriteCoors.z = 0.95f * RwCameraGetFarClipPlane(Scene.camera); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(aCoronas[i].texture)); @@ -320,7 +320,7 @@ CCoronas::Render(void) float f = 1.0f - aCoronas[i].someAngle*2.0f/PI; float wscale = 6.0f*sq(sq(sq(f))) + 0.5f; float hscale = 0.35f - (wscale - 0.5f) * 0.06f; - hscale = max(hscale, 0.15f); + hscale = Max(hscale, 0.15f); CSprite::RenderOneXLUSprite(spriteCoors.x, spriteCoors.y, spriteCoors.z, spritew * aCoronas[i].size * wscale, @@ -467,7 +467,7 @@ CCoronas::RenderReflections(void) float spritew, spriteh; if(CSprite::CalcScreenCoors(coors, spriteCoors, &spritew, &spriteh, true)){ float drawDist = 0.75f * aCoronas[i].drawDist; - drawDist = min(drawDist, 50.0f); + drawDist = Min(drawDist, 50.0f); if(spriteCoors.z < drawDist){ float fadeDistance = drawDist / 2.0f; float distanceFade = spriteCoors.z < fadeDistance ? 1.0f : 1.0f - (spriteCoors.z - fadeDistance)/fadeDistance; @@ -546,25 +546,25 @@ CRegisteredCorona::Update(void) (CCoronas::SunBlockedByClouds && id == CCoronas::SUN_CORONA || !CWorld::GetIsLineOfSightClear(coors, TheCamera.GetPosition(), true, false, false, false, false, false))){ // Corona is blocked, fade out - fadeAlpha = max(fadeAlpha - 15.0f*CTimer::GetTimeStep(), 0.0f); + fadeAlpha = Max(fadeAlpha - 15.0f*CTimer::GetTimeStep(), 0.0f); }else if(offScreen){ // Same when off screen - fadeAlpha = max(fadeAlpha - 15.0f*CTimer::GetTimeStep(), 0.0f); + fadeAlpha = Max(fadeAlpha - 15.0f*CTimer::GetTimeStep(), 0.0f); }else{ // Visible if(alpha > fadeAlpha){ // fade in - fadeAlpha = min(fadeAlpha + 15.0f*CTimer::GetTimeStep(), alpha); + fadeAlpha = Min(fadeAlpha + 15.0f*CTimer::GetTimeStep(), alpha); if(CCoronas::bChangeBrightnessImmediately) fadeAlpha = alpha; }else if(alpha < fadeAlpha){ // too visible, decrease alpha but not below alpha - fadeAlpha = max(fadeAlpha - 15.0f*CTimer::GetTimeStep(), alpha); + fadeAlpha = Max(fadeAlpha - 15.0f*CTimer::GetTimeStep(), alpha); } // darken scene when the sun is visible if(id == CCoronas::SUN_CORONA) - CCoronas::LightsMult = max(CCoronas::LightsMult - CTimer::GetTimeStep()*0.06f, 0.6f); + CCoronas::LightsMult = Max(CCoronas::LightsMult - CTimer::GetTimeStep()*0.06f, 0.6f); } // remove if invisible diff --git a/src/render/Lights.cpp b/src/render/Lights.cpp index cd83a898..85d7ba13 100644 --- a/src/render/Lights.cpp +++ b/src/render/Lights.cpp @@ -37,9 +37,9 @@ SetLightsWithTimeOfDayColour(RpWorld *) AmbientLightColourForFrame.green = 1.0f; AmbientLightColourForFrame.blue = 1.0f; } - AmbientLightColourForFrame_PedsCarsAndObjects.red = min(1.0f, AmbientLightColourForFrame.red*1.3f); - AmbientLightColourForFrame_PedsCarsAndObjects.green = min(1.0f, AmbientLightColourForFrame.green*1.3f); - AmbientLightColourForFrame_PedsCarsAndObjects.blue = min(1.0f, AmbientLightColourForFrame.blue*1.3f); + AmbientLightColourForFrame_PedsCarsAndObjects.red = Min(1.0f, AmbientLightColourForFrame.red*1.3f); + AmbientLightColourForFrame_PedsCarsAndObjects.green = Min(1.0f, AmbientLightColourForFrame.green*1.3f); + AmbientLightColourForFrame_PedsCarsAndObjects.blue = Min(1.0f, AmbientLightColourForFrame.blue*1.3f); RpLightSetColor(pAmbient, &AmbientLightColourForFrame); } @@ -70,16 +70,16 @@ SetLightsWithTimeOfDayColour(RpWorld *) float f1 = 2.0f * (CMenuManager::m_PrefsBrightness/256.0f - 1.0f) * 0.6f + 1.0f; float f2 = 3.0f * (CMenuManager::m_PrefsBrightness/256.0f - 1.0f) * 0.6f + 1.0f; - AmbientLightColourForFrame.red = min(1.0f, AmbientLightColourForFrame.red * f2); - AmbientLightColourForFrame.green = min(1.0f, AmbientLightColourForFrame.green * f2); - AmbientLightColourForFrame.blue = min(1.0f, AmbientLightColourForFrame.blue * f2); - AmbientLightColourForFrame_PedsCarsAndObjects.red = min(1.0f, AmbientLightColourForFrame_PedsCarsAndObjects.red * f1); - AmbientLightColourForFrame_PedsCarsAndObjects.green = min(1.0f, AmbientLightColourForFrame_PedsCarsAndObjects.green * f1); - AmbientLightColourForFrame_PedsCarsAndObjects.blue = min(1.0f, AmbientLightColourForFrame_PedsCarsAndObjects.blue * f1); + AmbientLightColourForFrame.red = Min(1.0f, AmbientLightColourForFrame.red * f2); + AmbientLightColourForFrame.green = Min(1.0f, AmbientLightColourForFrame.green * f2); + AmbientLightColourForFrame.blue = Min(1.0f, AmbientLightColourForFrame.blue * f2); + AmbientLightColourForFrame_PedsCarsAndObjects.red = Min(1.0f, AmbientLightColourForFrame_PedsCarsAndObjects.red * f1); + AmbientLightColourForFrame_PedsCarsAndObjects.green = Min(1.0f, AmbientLightColourForFrame_PedsCarsAndObjects.green * f1); + AmbientLightColourForFrame_PedsCarsAndObjects.blue = Min(1.0f, AmbientLightColourForFrame_PedsCarsAndObjects.blue * f1); #ifdef FIX_BUGS - DirectionalLightColourForFrame.red = min(1.0f, DirectionalLightColourForFrame.red * f1); - DirectionalLightColourForFrame.green = min(1.0f, DirectionalLightColourForFrame.green * f1); - DirectionalLightColourForFrame.blue = min(1.0f, DirectionalLightColourForFrame.blue * f1); + DirectionalLightColourForFrame.red = Min(1.0f, DirectionalLightColourForFrame.red * f1); + DirectionalLightColourForFrame.green = Min(1.0f, DirectionalLightColourForFrame.green * f1); + DirectionalLightColourForFrame.blue = Min(1.0f, DirectionalLightColourForFrame.blue * f1); #else DirectionalLightColourForFrame.red = min(1.0f, AmbientLightColourForFrame.red * f1); DirectionalLightColourForFrame.green = min(1.0f, AmbientLightColourForFrame.green * f1); @@ -193,7 +193,7 @@ AddAnExtraDirectionalLight(RpWorld *world, float dirx, float diry, float dirz, f RwRGBAReal color; RwV3d *dir; - strength = max(max(red, green), blue); + strength = Max(Max(red, green), blue); n = -1; if(NumExtraDirLightsInWorld < NUMEXTRADIRECTIONALS) n = NumExtraDirLightsInWorld; @@ -221,7 +221,7 @@ AddAnExtraDirectionalLight(RpWorld *world, float dirx, float diry, float dirz, f RwFrameUpdateObjects(RpLightGetFrame(pExtraDirectionals[n])); RpLightSetFlags(pExtraDirectionals[n], rpLIGHTLIGHTATOMICS); LightStrengths[n] = strength; - NumExtraDirLightsInWorld = min(NumExtraDirLightsInWorld+1, NUMEXTRADIRECTIONALS); + NumExtraDirLightsInWorld = Min(NumExtraDirLightsInWorld+1, NUMEXTRADIRECTIONALS); } void diff --git a/src/render/ParticleMgr.cpp b/src/render/ParticleMgr.cpp index 7a1804de..9381787c 100644 --- a/src/render/ParticleMgr.cpp +++ b/src/render/ParticleMgr.cpp @@ -91,7 +91,7 @@ void cParticleSystemMgr::LoadParticleData() break; case CFG_PARAM_INITIAL_COLOR_VARIATION: - entry->m_InitialColorVariation = min(atoi(value), 100); + entry->m_InitialColorVariation = Min(atoi(value), 100); break; case CFG_PARAM_FADE_DESTINATION_COLOR_R: diff --git a/src/render/PointLights.cpp b/src/render/PointLights.cpp index a015ec54..8e942ce6 100644 --- a/src/render/PointLights.cpp +++ b/src/render/PointLights.cpp @@ -98,7 +98,7 @@ CPointLights::GenerateLightsAffectingObject(CVector *objCoors) if(aLights[i].type == LIGHT_DIRECTIONAL){ float dot = -DotProduct(dir, aLights[i].dir); - intensity *= max((dot-0.5f)*2.0f, 0.0f); + intensity *= Max((dot-0.5f)*2.0f, 0.0f); } if(intensity > 0.0f) diff --git a/src/render/Shadows.cpp b/src/render/Shadows.cpp index ccdc457c..b5147f02 100644 --- a/src/render/Shadows.cpp +++ b/src/render/Shadows.cpp @@ -4,7 +4,7 @@ #include "TxdStore.h" #include "Timer.h" #include "Camera.h" -#include "TimeCycle.h" +#include "Timecycle.h" #include "CutsceneMgr.h" #include "Automobile.h" #include "Ped.h" @@ -727,10 +727,10 @@ CShadows::RenderStoredShadows(void) float fStartY = shadowPos.y - fHeight; float fEndY = shadowPos.y + fHeight; - int32 nStartX = max(CWorld::GetSectorIndexX(fStartX), 0); - int32 nStartY = max(CWorld::GetSectorIndexY(fStartY), 0); - int32 nEndX = min(CWorld::GetSectorIndexX(fEndX), NUMSECTORS_X-1); - int32 nEndY = min(CWorld::GetSectorIndexY(fEndY), NUMSECTORS_Y-1); + int32 nStartX = Max(CWorld::GetSectorIndexX(fStartX), 0); + int32 nStartY = Max(CWorld::GetSectorIndexY(fStartY), 0); + int32 nEndX = Min(CWorld::GetSectorIndexX(fEndX), NUMSECTORS_X-1); + int32 nEndY = Min(CWorld::GetSectorIndexY(fEndY), NUMSECTORS_Y-1); CWorld::AdvanceCurrentScanCode(); @@ -873,10 +873,10 @@ CShadows::GeneratePolysForStaticShadow(int16 nStaticShadowID) float fStartY = shadowPos.y - fHeight; float fEndY = shadowPos.y + fHeight; - int32 nStartX = max(CWorld::GetSectorIndexX(fStartX), 0); - int32 nStartY = max(CWorld::GetSectorIndexY(fStartY), 0); - int32 nEndX = min(CWorld::GetSectorIndexX(fEndX), NUMSECTORS_X-1); - int32 nEndY = min(CWorld::GetSectorIndexY(fEndY), NUMSECTORS_Y-1); + int32 nStartX = Max(CWorld::GetSectorIndexX(fStartX), 0); + int32 nStartY = Max(CWorld::GetSectorIndexY(fStartY), 0); + int32 nEndX = Min(CWorld::GetSectorIndexX(fEndX), NUMSECTORS_X-1); + int32 nEndY = Min(CWorld::GetSectorIndexY(fEndY), NUMSECTORS_Y-1); CWorld::AdvanceCurrentScanCode(); @@ -1016,11 +1016,11 @@ CShadows::CastShadowEntity(CEntity *pEntity, float fStartX, float fStartY, floa Points[3].x = (fLengthRight - fFrontRight) - fSideRight; Points[3].y = (fLengthForward - fFrontForward) - fSideForward; - float MinX = min(min(Points[0].x, Points[1].x), min(Points[2].x, Points[3].x)); - float MaxX = max(max(Points[0].x, Points[1].x), max(Points[2].x, Points[3].x)); + float MinX = Min(Min(Points[0].x, Points[1].x), Min(Points[2].x, Points[3].x)); + float MaxX = Max(Max(Points[0].x, Points[1].x), Max(Points[2].x, Points[3].x)); - float MinY = min(min(Points[0].y, Points[1].y), min(Points[2].y, Points[3].y)); - float MaxY = max(max(Points[0].y, Points[1].y), max(Points[2].y, Points[3].y)); + float MinY = Min(Min(Points[0].y, Points[1].y), Min(Points[2].y, Points[3].y)); + float MaxY = Max(Max(Points[0].y, Points[1].y), Max(Points[2].y, Points[3].y)); float MaxZ = pPosn->z - pEntity->GetPosition().z; float MinZ = MaxZ - fZDistance; @@ -1767,7 +1767,7 @@ CShadows::RenderIndicatorShadow(uint32 nID, uint8 ShadowType, RwTexture *pTextur { ASSERT(pPosn != NULL); - C3dMarkers::PlaceMarkerSet(nID, _TODOCONST(4), *pPosn, max(fFrontX, -fSideY), + C3dMarkers::PlaceMarkerSet(nID, _TODOCONST(4), *pPosn, Max(fFrontX, -fSideY), 0, 128, 255, 128, 2048, 0.2f, 0); } diff --git a/src/render/Timecycle.cpp b/src/render/Timecycle.cpp index 7ab3e91e..0113c001 100644 --- a/src/render/Timecycle.cpp +++ b/src/render/Timecycle.cpp @@ -290,7 +290,7 @@ CTimeCycle::Update(void) TheCamera.SetMotionBlur(m_fCurrentBlurRed, m_fCurrentBlurGreen, m_fCurrentBlurBlue, m_fCurrentBlurAlpha, MBLUR_NORMAL); if(m_FogReduction != 0) - m_fCurrentFarClip = max(m_fCurrentFarClip, m_FogReduction/64.0f * 650.0f); + m_fCurrentFarClip = Max(m_fCurrentFarClip, m_FogReduction/64.0f * 650.0f); m_nCurrentFogColourRed = (m_nCurrentSkyTopRed + 2*m_nCurrentSkyBottomRed) / 3; m_nCurrentFogColourGreen = (m_nCurrentSkyTopGreen + 2*m_nCurrentSkyBottomGreen) / 3; m_nCurrentFogColourBlue = (m_nCurrentSkyTopBlue + 2*m_nCurrentSkyBottomBlue) / 3; @@ -311,9 +311,9 @@ CTimeCycle::Update(void) if(TheCamera.GetForward().z < -0.9f || !CWeather::bScriptsForceRain && (CCullZones::PlayerNoRain() || CCullZones::CamNoRain() || CCutsceneMgr::IsRunning())) - m_FogReduction = min(m_FogReduction+1, 64); + m_FogReduction = Min(m_FogReduction+1, 64); else - m_FogReduction = max(m_FogReduction-1, 0); + m_FogReduction = Max(m_FogReduction-1, 0); } STARTPATCHES diff --git a/src/render/WaterLevel.cpp b/src/render/WaterLevel.cpp index 48e8f83b..c711c8c8 100644 --- a/src/render/WaterLevel.cpp +++ b/src/render/WaterLevel.cpp @@ -9,7 +9,7 @@ #include "Boat.h" #include "World.h" #include "General.h" -#include "TimeCycle.h" +#include "Timecycle.h" #include "ZoneCull.h" #include "Clock.h" #include "Particle.h" @@ -979,7 +979,7 @@ CWaterLevel::RenderOneWavySector(float fX, float fY, float fZ, RwRGBA const &col SMALL_SECTOR_SIZE / 2, apBoatList) ) { - float fWakeColor = fAdd1 - max(255.0f - float(color.blue + color.red + color.green) / 3, fAdd2); + float fWakeColor = fAdd1 - Max(255.0f - float(color.blue + color.red + color.green) / 3, fAdd2); RpGeometry *wavyGeometry = RpAtomicGetGeometry(ms_pWavyAtomic); RpGeometry *geom = apGeomArray[nGeomUsed++]; @@ -1035,9 +1035,9 @@ CWaterLevel::RenderOneWavySector(float fX, float fY, float fZ, RwRGBA const &col RwRGBAAssign(&wakeColor, &color); - wakeColor.red = min(color.red + int32(fWakeColor * fRedMult * fDistMult), 255); - wakeColor.green = min(color.green + int32(fWakeColor * fGreenMult * fDistMult), 255); - wakeColor.blue = min(color.blue + int32(fWakeColor * fBlueMult * fDistMult), 255); + wakeColor.red = Min(color.red + int32(fWakeColor * fRedMult * fDistMult), 255); + wakeColor.green = Min(color.green + int32(fWakeColor * fGreenMult * fDistMult), 255); + wakeColor.blue = Min(color.blue + int32(fWakeColor * fBlueMult * fDistMult), 255); RwRGBAAssign(&geomPreLights[9*i+j], &wakeColor); @@ -1114,7 +1114,7 @@ CWaterLevel::CalcDistanceToWater(float fX, float fY) fSectorY + SMALL_SECTOR_SIZE - fY ); - fDistSqr = min(vecDist.MagnitudeSqr(), fDistSqr); + fDistSqr = Min(vecDist.MagnitudeSqr(), fDistSqr); } } } diff --git a/src/vehicles/Automobile.cpp b/src/vehicles/Automobile.cpp index 581b5815..0fe59cd0 100644 --- a/src/vehicles/Automobile.cpp +++ b/src/vehicles/Automobile.cpp @@ -7,9 +7,13 @@ #include "ModelIndices.h" #include "VisibilityPlugins.h" #include "DMAudio.h" +<<<<<<< HEAD #include "Clock.h" #include "TimeCycle.h" #include "ZoneCull.h" +======= +#include "Timecycle.h" +>>>>>>> More audio ped #include "Camera.h" #include "Darkel.h" #include "Rubbish.h" @@ -691,7 +695,7 @@ CAutomobile::ProcessControl(void) if(m_aSuspensionSpringRatio[i] < 1.0f) m_aWheelTimer[i] = 4.0f; else - m_aWheelTimer[i] = max(m_aWheelTimer[i]-CTimer::GetTimeStep(), 0.0f); + m_aWheelTimer[i] = Max(m_aWheelTimer[i]-CTimer::GetTimeStep(), 0.0f); if(m_aWheelTimer[i] > 0.0f){ m_nWheelsOnGround++; @@ -1009,7 +1013,7 @@ CAutomobile::ProcessControl(void) if(m_status != STATUS_PLAYER && m_status != STATUS_PLAYER_REMOTE && m_status != STATUS_PHYSICS){ if(GetModelIndex() == MI_MIAMI_RCRAIDER || GetModelIndex() == MI_MIAMI_SPARROW) - m_aWheelSpeed[0] = max(m_aWheelSpeed[0]-0.0005f, 0.0f); + m_aWheelSpeed[0] = Max(m_aWheelSpeed[0]-0.0005f, 0.0f); }else if((GetModelIndex() == MI_DODO || CVehicle::bAllDodosCheat) && m_vecMoveSpeed.Magnitude() > 0.0f && CTimer::GetTimeStep() > 0.0f){ FlyingControl(FLIGHT_MODEL_DODO); @@ -1017,7 +1021,7 @@ CAutomobile::ProcessControl(void) FlyingControl(FLIGHT_MODEL_HELI); }else if(GetModelIndex() == MI_MIAMI_RCRAIDER || GetModelIndex() == MI_MIAMI_SPARROW || bAllCarCheat){ if(CPad::GetPad(0)->GetCircleJustDown()) - m_aWheelSpeed[0] = max(m_aWheelSpeed[0]-0.03f, 0.0f); + m_aWheelSpeed[0] = Max(m_aWheelSpeed[0]-0.03f, 0.0f); if(m_aWheelSpeed[0] < 0.22f) m_aWheelSpeed[0] += 0.0001f; if(m_aWheelSpeed[0] > 0.15f) @@ -1129,10 +1133,10 @@ CAutomobile::ProcessControl(void) if(speed > sq(0.1f)){ speed = Sqrt(speed); if(suspShake > 0.0f){ - uint8 freq = min(200.0f*suspShake*speed*2000.0f/m_fMass + 100.0f, 250.0f); + uint8 freq = Min(200.0f*suspShake*speed*2000.0f/m_fMass + 100.0f, 250.0f); CPad::GetPad(0)->StartShake(20000.0f*CTimer::GetTimeStep()/freq, freq); }else{ - uint8 freq = min(200.0f*surfShake*speed*2000.0f/m_fMass + 40.0f, 145.0f); + uint8 freq = Min(200.0f*surfShake*speed*2000.0f/m_fMass + 40.0f, 145.0f); CPad::GetPad(0)->StartShake(5000.0f*CTimer::GetTimeStep()/freq, freq); } } @@ -2586,7 +2590,7 @@ CAutomobile::HydraulicControl(void) float minz = pos.z + extendedLowerLimit - wheelRadius; if(minz < specialColModel->boundingBox.min.z) specialColModel->boundingBox.min.z = minz; - float radius = max(specialColModel->boundingBox.min.Magnitude(), specialColModel->boundingBox.max.Magnitude()); + float radius = Max(specialColModel->boundingBox.min.Magnitude(), specialColModel->boundingBox.max.Magnitude()); if(specialColModel->boundingSphere.radius < radius) specialColModel->boundingSphere.radius = radius; @@ -2685,10 +2689,10 @@ CAutomobile::HydraulicControl(void) float front = -rear; float right = CPad::GetPad(0)->GetCarGunLeftRight()/128.0f; float left = -right; - suspChange[CARWHEEL_FRONT_LEFT] = max(front+left, 0.0f); - suspChange[CARWHEEL_REAR_LEFT] = max(rear+left, 0.0f); - suspChange[CARWHEEL_FRONT_RIGHT] = max(front+right, 0.0f); - suspChange[CARWHEEL_REAR_RIGHT] = max(rear+right, 0.0f); + suspChange[CARWHEEL_FRONT_LEFT] = Max(front+left, 0.0f); + suspChange[CARWHEEL_REAR_LEFT] = Max(rear+left, 0.0f); + suspChange[CARWHEEL_FRONT_RIGHT] = Max(front+right, 0.0f); + suspChange[CARWHEEL_REAR_RIGHT] = Max(rear+right, 0.0f); if(m_hydraulicState < 100){ // Lowered, move wheels up @@ -2804,7 +2808,7 @@ CAutomobile::ProcessBuoyancy(void) ApplyTurnForce(impulse, point); CVector initialSpeed = m_vecMoveSpeed; - float timeStep = max(CTimer::GetTimeStep(), 0.01f); + float timeStep = Max(CTimer::GetTimeStep(), 0.01f); float impulseRatio = impulse.z / (GRAVITY * m_fMass * timeStep); float waterResistance = Pow(1.0f - 0.05f*impulseRatio, CTimer::GetTimeStep()); m_vecMoveSpeed *= waterResistance; @@ -2897,7 +2901,7 @@ CAutomobile::ProcessBuoyancy(void) float fSpeed = vSpeed.MagnitudeSqr(); if(fSpeed > sq(0.05f)){ fSpeed = Sqrt(fSpeed); - float size = min((fSpeed < 0.15f ? 0.25f : 0.75f)*fSpeed, 0.6f); + float size = Min((fSpeed < 0.15f ? 0.25f : 0.75f)*fSpeed, 0.6f); CVector right = 0.2f*fSpeed*GetRight() + 0.2f*vSpeed; CParticle::AddParticle(PARTICLE_PED_SPLASH, @@ -2979,11 +2983,11 @@ CAutomobile::DoDriveByShootings(void) // TODO: what is this? if(!lookingLeft && m_weaponDoorTimerLeft > 0.0f){ - m_weaponDoorTimerLeft = max(m_weaponDoorTimerLeft - CTimer::GetTimeStep()*0.1f, 0.0f); + m_weaponDoorTimerLeft = Max(m_weaponDoorTimerLeft - CTimer::GetTimeStep()*0.1f, 0.0f); ProcessOpenDoor(CAR_DOOR_LF, NUM_ANIMS, m_weaponDoorTimerLeft); } if(!lookingRight && m_weaponDoorTimerRight > 0.0f){ - m_weaponDoorTimerRight = max(m_weaponDoorTimerRight - CTimer::GetTimeStep()*0.1f, 0.0f); + m_weaponDoorTimerRight = Max(m_weaponDoorTimerRight - CTimer::GetTimeStep()*0.1f, 0.0f); ProcessOpenDoor(CAR_DOOR_RF, NUM_ANIMS, m_weaponDoorTimerRight); } } @@ -3131,7 +3135,7 @@ CAutomobile::VehicleDamage(float impulse, uint16 damagedPiece) FindPlayerPed()->SetWantedLevelNoDrop(1); if(m_status == STATUS_PLAYER && impulse > 50.0f){ - uint8 freq = min(0.4f*impulse*2000.0f/m_fMass + 100.0f, 250.0f); + uint8 freq = Min(0.4f*impulse*2000.0f/m_fMass + 100.0f, 250.0f); CPad::GetPad(0)->StartShake(40000/freq, freq); } @@ -3284,7 +3288,7 @@ CAutomobile::VehicleDamage(float impulse, uint16 damagedPiece) if(m_pDamageEntity && m_pDamageEntity == FindPlayerVehicle() && impulse > 10.0f){ int money = (doubleMoney ? 2 : 1) * impulse*pHandling->nMonetaryValue/1000000.0f; - money = min(money, 40); + money = Min(money, 40); if(money > 2){ sprintf(gString, "$%d", money); CWorld::Players[CWorld::PlayerInFocus].m_nMoney += money; @@ -3987,7 +3991,7 @@ CAutomobile::SetupSuspensionLines(void) // adjust col model to include suspension lines if(colModel->boundingBox.min.z > colModel->lines[0].p1.z) colModel->boundingBox.min.z = colModel->lines[0].p1.z; - float radius = max(colModel->boundingBox.min.Magnitude(), colModel->boundingBox.max.Magnitude()); + float radius = Max(colModel->boundingBox.min.Magnitude(), colModel->boundingBox.max.Magnitude()); if(colModel->boundingSphere.radius < radius) colModel->boundingSphere.radius = radius; @@ -4479,8 +4483,16 @@ CAutomobile::SetAllTaxiLights(bool set) class CAutomobile_ : public CAutomobile { public: +<<<<<<< HEAD +<<<<<<< HEAD void ctor(int32 id, uint8 CreatedBy) { ::new (this) CAutomobile(id, CreatedBy); } void dtor() { CAutomobile::~CAutomobile(); } +======= + void dtor() { this->~CAutomobile(); } +>>>>>>> More audio ped +======= + void dtor() { CAutomobile::~CAutomobile(); } +>>>>>>> fix void SetModelIndex_(uint32 id) { CAutomobile::SetModelIndex(id); } void ProcessControl_(void) { CAutomobile::ProcessControl(); } void Teleport_(CVector v) { CAutomobile::Teleport(v); } diff --git a/src/vehicles/Automobile.cpp.autosave b/src/vehicles/Automobile.cpp.autosave new file mode 100644 index 00000000..140633b8 --- /dev/null +++ b/src/vehicles/Automobile.cpp.autosave @@ -0,0 +1,4560 @@ +#include "common.h" +#include "main.h" +#include "patcher.h" +#include "General.h" +#include "RwHelper.h" +#include "Pad.h" +#include "ModelIndices.h" +#include "VisibilityPlugins.h" +#include "DMAudio.h" +#include "Clock.h" +#include "Timecycle.h" +#include "ZoneCull.h" +#include "Camera.h" +#include "Darkel.h" +#include "Rubbish.h" +#include "Fire.h" +#include "Explosion.h" +#include "Particle.h" +#include "ParticleObject.h" +#include "Antennas.h" +#include "Skidmarks.h" +#include "Shadows.h" +#include "PointLights.h" +#include "Coronas.h" +#include "SpecialFX.h" +#include "WaterCannon.h" +#include "WaterLevel.h" +#include "Floater.h" +#include "World.h" +#include "SurfaceTable.h" +#include "Weather.h" +#include "HandlingMgr.h" +#include "Record.h" +#include "Remote.h" +#include "Population.h" +#include "CarCtrl.h" +#include "CarAI.h" +#include "Garages.h" +#include "PathFind.h" +#include "AnimManager.h" +#include "RpAnimBlend.h" +#include "Ped.h" +#include "PlayerPed.h" +#include "Object.h" +#include "Automobile.h" + +bool bAllCarCheat; // unused + +RwObject *GetCurrentAtomicObjectCB(RwObject *object, void *data); + +bool &CAutomobile::m_sAllTaxiLights = *(bool*)0x95CD21; + +CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) + : CVehicle(CreatedBy) +{ + int i; + + m_vehType = VEHICLE_TYPE_CAR; + + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); + m_fFireBlowUpTimer = 0.0f; + field_4E0 = 0; + bTaxiLight = m_sAllTaxiLights; + m_auto_flagA20 = false; + m_auto_flagA40 = false; + bWaterTight = false; + + SetModelIndex(id); + + pHandling = mod_HandlingManager.GetHandlingData((eHandlingId)mi->m_handlingId); + + field_49C = 20.0f; + field_4D8 = 0; + + mi->ChooseVehicleColour(m_currentColour1, m_currentColour2); + + bIsVan = !!(pHandling->Flags & HANDLING_IS_VAN); + bIsBig = !!(pHandling->Flags & HANDLING_IS_BIG); + bIsBus = !!(pHandling->Flags & HANDLING_IS_BUS); + bLowVehicle = !!(pHandling->Flags & HANDLING_IS_LOW); + + // Doors + if(bIsBus){ + Doors[DOOR_FRONT_LEFT].Init(-HALFPI, 0.0f, 0, 2); + Doors[DOOR_FRONT_RIGHT].Init(0.0f, HALFPI, 1, 2); + }else{ + Doors[DOOR_FRONT_LEFT].Init(-PI*0.4f, 0.0f, 0, 2); + Doors[DOOR_FRONT_RIGHT].Init(0.0f, PI*0.4f, 1, 2); + } + if(bIsVan){ + Doors[DOOR_REAR_LEFT].Init(-HALFPI, 0.0f, 1, 2); + Doors[DOOR_REAR_RIGHT].Init(0.0f, HALFPI, 0, 2); + }else{ + Doors[DOOR_REAR_LEFT].Init(-PI*0.4f, 0.0f, 0, 2); + Doors[DOOR_REAR_RIGHT].Init(0.0f, PI*0.4f, 1, 2); + } + if(pHandling->Flags & HANDLING_REV_BONNET) + Doors[DOOR_BONNET].Init(-PI*0.3f, 0.0f, 1, 0); + else + Doors[DOOR_BONNET].Init(0.0f, PI*0.3f, 1, 0); + if(pHandling->Flags & HANDLING_HANGING_BOOT) + Doors[DOOR_BOOT].Init(PI*0.4f, 0.0f, 0, 0); + else if(pHandling->Flags & HANDLING_TAILGATE_BOOT) + Doors[DOOR_BOOT].Init(0.0, HALFPI, 1, 0); + else + Doors[DOOR_BOOT].Init(-PI*0.3f, 0.0f, 1, 0); + if(pHandling->Flags & HANDLING_NO_DOORS){ + Damage.SetDoorStatus(DOOR_FRONT_LEFT, DOOR_STATUS_MISSING); + Damage.SetDoorStatus(DOOR_FRONT_RIGHT, DOOR_STATUS_MISSING); + Damage.SetDoorStatus(DOOR_REAR_LEFT, DOOR_STATUS_MISSING); + Damage.SetDoorStatus(DOOR_REAR_RIGHT, DOOR_STATUS_MISSING); + } + + for(i = 0; i < 6; i++) + m_randomValues[i] = CGeneral::GetRandomNumberInRange(-0.15f, 0.15f); + + m_fMass = pHandling->fMass; + m_fTurnMass = pHandling->fTurnMass; + m_vecCentreOfMass = pHandling->CentreOfMass; + m_fAirResistance = pHandling->Dimension.x*pHandling->Dimension.z/m_fMass; + m_fElasticity = 0.05f; + m_fBuoyancy = pHandling->fBuoyancy; + + m_nBusDoorTimerEnd = 0; + m_nBusDoorTimerStart = 0; + + m_fSteerAngle = 0.0f; + m_fGasPedal = 0.0f; + m_fBrakePedal = 0.0f; + m_pSetOnFireEntity = nil; + field_594 = 0; + bNotDamagedUpsideDown = false; + bMoreResistantToDamage = false; + m_fVelocityChangeForAudio = 0.0f; + m_hydraulicState = 0; + + for(i = 0; i < 4; i++){ + m_aGroundPhysical[i] = nil; + m_aGroundOffset[i] = CVector(0.0f, 0.0f, 0.0f); + m_aSuspensionSpringRatio[i] = 1.0f; + m_aSuspensionSpringRatioPrev[i] = m_aSuspensionSpringRatio[i]; + m_aWheelTimer[i] = 0.0f; + m_aWheelRotation[i] = 0.0f; + m_aWheelSpeed[i] = 0.0f; + m_aWheelState[i] = WHEEL_STATE_NORMAL; + m_aWheelSkidmarkMuddy[i] = false; + m_aWheelSkidmarkBloody[i] = false; + } + + m_nWheelsOnGround = 0; + m_nDriveWheelsOnGround = 0; + m_nDriveWheelsOnGroundPrev = 0; + m_fHeightAboveRoad = 0.0f; + m_fTraction = 1.0f; + + CColModel *colModel = mi->GetColModel(); + if(colModel->lines == nil){ + colModel->lines = (CColLine*)RwMalloc(4*sizeof(CColLine)); + colModel->numLines = 4; + } + + SetupSuspensionLines(); + + m_status = STATUS_SIMPLE; + bUseCollisionRecords = true; + + m_nNumPassengers = 0; + + m_bombType = CARBOMB_NONE; + bHadDriver = false; + m_pBombRigger = nil; + + if(m_nDoorLock == CARLOCK_UNLOCKED && + (id == MI_POLICE || id == MI_ENFORCER || id == MI_RHINO)) + m_nDoorLock = CARLOCK_LOCKED_INITIALLY; + + m_fCarGunLR = 0.0f; + m_fCarGunUD = 0.05f; + m_fPropellerRotation = 0.0f; + m_weaponDoorTimerLeft = 0.0f; + m_weaponDoorTimerRight = m_weaponDoorTimerLeft; + + if(GetModelIndex() == MI_DODO){ + RpAtomicSetFlags(GetFirstObject(m_aCarNodes[CAR_WHEEL_LF]), 0); + CMatrix mat1; + mat1.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); + CMatrix mat2(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF])); + mat1.GetPosition() += CVector(mat2.GetPosition().x + 0.1f, 0.0f, mat2.GetPosition().z); + mat1.UpdateRW(); + }else if(GetModelIndex() == MI_MIAMI_SPARROW || GetModelIndex() == MI_MIAMI_RCRAIDER){ + RpAtomicSetFlags(GetFirstObject(m_aCarNodes[CAR_WHEEL_LF]), 0); + RpAtomicSetFlags(GetFirstObject(m_aCarNodes[CAR_WHEEL_RF]), 0); + RpAtomicSetFlags(GetFirstObject(m_aCarNodes[CAR_WHEEL_LB]), 0); + RpAtomicSetFlags(GetFirstObject(m_aCarNodes[CAR_WHEEL_RB]), 0); + }else if(GetModelIndex() == MI_RHINO){ + bExplosionProof = true; + bBulletProof = true; + } +} + + +void +CAutomobile::SetModelIndex(uint32 id) +{ + CVehicle::SetModelIndex(id); + SetupModelNodes(); +} + +CVector vecDAMAGE_ENGINE_POS_SMALL(-0.1f, -0.1f, 0.0f); +CVector vecDAMAGE_ENGINE_POS_BIG(-0.5f, -0.3f, 0.0f); + +void +CAutomobile::ProcessControl(void) +{ + int i; + CColModel *colModel; + + if(bUsingSpecialColModel) + colModel = &CWorld::Players[CWorld::PlayerInFocus].m_ColModel; + else + colModel = GetColModel(); + bWarnedPeds = false; + + // skip if the collision isn't for the current level + if(colModel->level > LEVEL_NONE && colModel->level != CCollision::ms_collisionInMemory) + return; + + // Improve grip of vehicles in certain cases + bool strongGrip1 = false; + bool strongGrip2 = false; + if(FindPlayerVehicle() && this != FindPlayerVehicle()){ + switch(AutoPilot.m_nCarMission){ + case MISSION_RAMPLAYER_FARAWAY: + case MISSION_RAMPLAYER_CLOSE: + case MISSION_BLOCKPLAYER_FARAWAY: + case MISSION_BLOCKPLAYER_CLOSE: + if(FindPlayerSpeed().Magnitude() > 0.3f){ + strongGrip1 = true; + if(FindPlayerSpeed().Magnitude() > 0.4f){ + if(m_vecMoveSpeed.Magnitude() < 0.3f) + strongGrip2 = true; + }else{ + if((GetPosition() - FindPlayerCoors()).Magnitude() > 50.0f) + strongGrip2 = true; + } + } + } + } + + if(bIsBus) + ProcessAutoBusDoors(); + + ProcessCarAlarm(); + + // Scan if this car is committing a crime that the police can see + if(m_status != STATUS_ABANDONED && m_status != STATUS_WRECKED && + m_status != STATUS_PLAYER && m_status != STATUS_PLAYER_REMOTE && m_status != STATUS_PLAYER_DISABLED){ + switch(GetModelIndex()) + case MI_FBICAR: + case MI_POLICE: + case MI_ENFORCER: + case MI_SECURICA: + case MI_RHINO: + case MI_BARRACKS: + ScanForCrimes(); + } + + // Process driver + if(pDriver){ + if(!bHadDriver && m_bombType == CARBOMB_ONIGNITIONACTIVE){ + // If someone enters the car and there is a bomb, detonate + m_nBombTimer = 1000; + m_pBlowUpEntity = m_pBombRigger; + if(m_pBlowUpEntity) + m_pBlowUpEntity->RegisterReference((CEntity**)&m_pBlowUpEntity); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_BOMB_TICK, 1.0f); + } + bHadDriver = true; + + if(IsUpsideDown() && CanPedEnterCar()){ + if(!pDriver->IsPlayer() && + !(pDriver->m_leader && pDriver->m_leader->bInVehicle) && + pDriver->CharCreatedBy != MISSION_CHAR) + pDriver->SetObjective(OBJECTIVE_LEAVE_VEHICLE, this); + } + }else + bHadDriver = false; + + // Process passengers + if(m_nNumPassengers != 0 && IsUpsideDown() && CanPedEnterCar()){ + for(i = 0; i < m_nNumMaxPassengers; i++) + if(pPassengers[i]) + if(!pPassengers[i]->IsPlayer() && + !(pPassengers[i]->m_leader && pPassengers[i]->m_leader->bInVehicle) && + pPassengers[i]->CharCreatedBy != MISSION_CHAR) + pPassengers[i]->SetObjective(OBJECTIVE_LEAVE_VEHICLE, this); + } + + CRubbish::StirUp(this); + + // blend in clump + int clumpAlpha = CVisibilityPlugins::GetClumpAlpha((RpClump*)m_rwObject); + if(bFadeOut){ + clumpAlpha -= 8; + if(clumpAlpha < 0) + clumpAlpha = 0; + }else if(clumpAlpha < 255){ + clumpAlpha += 16; + if(clumpAlpha > 255) + clumpAlpha = 255; + } + CVisibilityPlugins::SetClumpAlpha((RpClump*)m_rwObject, clumpAlpha); + + AutoPilot.m_bSlowedDownBecauseOfCars = false; + AutoPilot.m_bSlowedDownBecauseOfPeds = false; + + // Set Center of Mass to make car more stable + if(strongGrip1 || bCheat3) + m_vecCentreOfMass.z = 0.3f*m_aSuspensionSpringLength[0] + -1.0*m_fHeightAboveRoad; + else if(pHandling->Flags & HANDLING_NONPLAYER_STABILISER && m_status == STATUS_PHYSICS) + m_vecCentreOfMass.z = pHandling->CentreOfMass.z - 0.2f*pHandling->Dimension.z; + else + m_vecCentreOfMass.z = pHandling->CentreOfMass.z; + + // Process depending on status + + bool playerRemote = false; + switch(m_status){ + case STATUS_PLAYER_REMOTE: + if(CPad::GetPad(0)->WeaponJustDown()){ + BlowUpCar(FindPlayerPed()); + CRemote::TakeRemoteControlledCarFromPlayer(); + } + + if(GetModelIndex() == MI_RCBANDIT){ + CVector pos = GetPosition(); + // FindPlayerCoors unused + if(RcbanditCheckHitWheels() || bIsInWater || CPopulation::IsPointInSafeZone(&pos)){ + if(CPopulation::IsPointInSafeZone(&pos)) + CGarages::TriggerMessage("HM2_5", -1, 5000, -1); + CRemote::TakeRemoteControlledCarFromPlayer(); + BlowUpCar(FindPlayerPed()); + } + } + + if(CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle == this) + playerRemote = true; + // fall through + case STATUS_PLAYER: + if(playerRemote || + pDriver && pDriver->GetPedState() != PED_EXIT_CAR && pDriver->GetPedState() != PED_DRAG_FROM_CAR){ + // process control input if controlled by player + if(playerRemote || pDriver->m_nPedType == PEDTYPE_PLAYER1) + ProcessControlInputs(0); + + PruneReferences(); + + if(m_status == STATUS_PLAYER && CRecordDataForChase::Status != RECORDSTATE_1) + DoDriveByShootings(); + } + break; + + case STATUS_SIMPLE: + CCarAI::UpdateCarAI(this); + CPhysical::ProcessControl(); + CCarCtrl::UpdateCarOnRails(this); + + m_nWheelsOnGround = 4; + m_nDriveWheelsOnGroundPrev = m_nDriveWheelsOnGround; + m_nDriveWheelsOnGround = 4; + + pHandling->Transmission.CalculateGearForSimpleCar(AutoPilot.m_fMaxTrafficSpeed/50.0f, m_nCurrentGear); + + { + float wheelRot = ProcessWheelRotation(WHEEL_STATE_NORMAL, GetForward(), m_vecMoveSpeed, 0.35f); + for(i = 0; i < 4; i++) + m_aWheelRotation[i] += wheelRot; + } + + PlayHornIfNecessary(); + ReduceHornCounter(); + bVehicleColProcessed = false; + // that's all we do for simple vehicles + return; + + case STATUS_PHYSICS: + CCarAI::UpdateCarAI(this); + CCarCtrl::SteerAICarWithPhysics(this); + PlayHornIfNecessary(); + break; + + case STATUS_ABANDONED: + m_fBrakePedal = 0.2f; + bIsHandbrakeOn = false; + + m_fSteerAngle = 0.0f; + m_fGasPedal = 0.0f; + m_nCarHornTimer = 0; + break; + + case STATUS_WRECKED: + m_fBrakePedal = 0.05f; + bIsHandbrakeOn = true; + + m_fSteerAngle = 0.0f; + m_fGasPedal = 0.0f; + m_nCarHornTimer = 0; + break; + + case STATUS_PLAYER_DISABLED: + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + + m_fSteerAngle = 0.0f; + m_fGasPedal = 0.0f; + m_nCarHornTimer = 0; + break; + } + + // what's going on here? + if(GetPosition().z < -0.6f && + Abs(m_vecMoveSpeed.x) < 0.05f && + Abs(m_vecMoveSpeed.y) < 0.05f) + m_vecTurnSpeed *= Pow(0.95f, CTimer::GetTimeStep()); + + // Skip physics if object is found to have been static recently + bool skipPhysics = false; + if(!bIsStuck && (m_status == STATUS_ABANDONED || m_status == STATUS_WRECKED)){ + bool makeStatic = false; + float moveSpeedLimit, turnSpeedLimit, distanceLimit; + + if(!bVehicleColProcessed && + m_vecMoveSpeed.IsZero() && + // BUG? m_aSuspensionSpringRatioPrev[3] is checked twice in the game. also, why 3? + m_aSuspensionSpringRatioPrev[3] != 1.0f) + makeStatic = true; + + if(m_status == STATUS_WRECKED){ + moveSpeedLimit = 0.006f; + turnSpeedLimit = 0.0015f; + distanceLimit = 0.015f; + }else{ + moveSpeedLimit = 0.003f; + turnSpeedLimit = 0.0009f; + distanceLimit = 0.005f; + } + + m_vecMoveSpeedAvg = (m_vecMoveSpeedAvg + m_vecMoveSpeed)/2.0f; + m_vecTurnSpeedAvg = (m_vecTurnSpeedAvg + m_vecTurnSpeed)/2.0f; + + if(m_vecMoveSpeedAvg.MagnitudeSqr() <= sq(moveSpeedLimit*CTimer::GetTimeStep()) && + m_vecTurnSpeedAvg.MagnitudeSqr() <= sq(turnSpeedLimit*CTimer::GetTimeStep()) && + m_fDistanceTravelled < distanceLimit || + makeStatic){ + m_nStaticFrames++; + + if(m_nStaticFrames > 10 || makeStatic) + if(!CCarCtrl::MapCouldMoveInThisArea(GetPosition().x, GetPosition().y)){ + if(!makeStatic || m_nStaticFrames > 10) + m_nStaticFrames = 10; + + skipPhysics = true; + + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + } + }else + m_nStaticFrames = 0; + } + + // Postpone + for(i = 0; i < 4; i++) + if(m_aGroundPhysical[i] && !CWorld::bForceProcessControl && m_aGroundPhysical[i]->bIsInSafePosition){ + bWasPostponed = true; + return; + } + + VehicleDamage(0.0f, 0); + + // special control + switch(GetModelIndex()){ + case MI_FIRETRUCK: + FireTruckControl(); + break; + case MI_RHINO: + TankControl(); + BlowUpCarsInPath(); + break; + case MI_YARDIE: + // beta also had esperanto here it seems + HydraulicControl(); + break; + default: + if(CVehicle::bCheat3){ + // Make vehicle jump when horn is sounded + if(m_status == STATUS_PLAYER && m_vecMoveSpeed.MagnitudeSqr() > sq(0.2f) && + // BUG: game checks [0] four times, instead of all wheels + m_aSuspensionSpringRatio[0] < 1.0f && + CPad::GetPad(0)->HornJustDown()){ + + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_1, 0.0f); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP, 1.0f); + + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, + m_aWheelColPoints[0].point + 0.5f*GetUp(), + 1.3f*m_vecMoveSpeed, nil, 2.5f); + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, + m_aWheelColPoints[0].point + 0.5f*GetUp(), + 1.2f*m_vecMoveSpeed, nil, 2.0f); + + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, + m_aWheelColPoints[2].point + 0.5f*GetUp(), + 1.3f*m_vecMoveSpeed, nil, 2.5f); + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, + m_aWheelColPoints[2].point + 0.5f*GetUp(), + 1.2f*m_vecMoveSpeed, nil, 2.0f); + + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, + m_aWheelColPoints[0].point + 0.5f*GetUp() - GetForward(), + 1.3f*m_vecMoveSpeed, nil, 2.5f); + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, + m_aWheelColPoints[0].point + 0.5f*GetUp() - GetForward(), + 1.2f*m_vecMoveSpeed, nil, 2.0f); + + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, + m_aWheelColPoints[2].point + 0.5f*GetUp() - GetForward(), + 1.3f*m_vecMoveSpeed, nil, 2.5f); + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, + m_aWheelColPoints[2].point + 0.5f*GetUp() - GetForward(), + 1.2f*m_vecMoveSpeed, nil, 2.0f); + + ApplyMoveForce(CVector(0.0f, 0.0f, 1.0f)*m_fMass*0.4f); + ApplyTurnForce(GetUp()*m_fMass*0.035f, GetForward()*1.0f); + } + } + break; + } + + float brake; + if(skipPhysics){ + bHasContacted = false; + bIsInSafePosition = false; + bWasPostponed = false; + bHasHitWall = false; + m_nCollisionRecords = 0; + bHasCollided = false; + bVehicleColProcessed = false; + m_nDamagePieceType = 0; + m_fDamageImpulse = 0.0f; + m_pDamageEntity = nil; + m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); + m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); + }else{ + + // This has to be done if ProcessEntityCollision wasn't called + if(!bVehicleColProcessed){ + CMatrix mat(GetMatrix()); + bIsStuck = false; + bHasContacted = false; + bIsInSafePosition = false; + bWasPostponed = false; + bHasHitWall = false; + m_fDistanceTravelled = 0.0f; + field_EF = false; + m_phy_flagA80 = false; + ApplyMoveSpeed(); + ApplyTurnSpeed(); + for(i = 0; CheckCollision() && i < 5; i++){ + GetMatrix() = mat; + ApplyMoveSpeed(); + ApplyTurnSpeed(); + } + bIsInSafePosition = true; + bIsStuck = false; + } + + CPhysical::ProcessControl(); + + ProcessBuoyancy(); + + // Rescale spring ratios, i.e. subtract wheel radius + for(i = 0; i < 4; i++){ + // wheel radius in relation to suspension line + float wheelRadius = 1.0f - m_aSuspensionSpringLength[i]/m_aSuspensionLineLength[i]; + // rescale such that 0.0 is fully compressed and 1.0 is fully extended + m_aSuspensionSpringRatio[i] = (m_aSuspensionSpringRatio[i]-wheelRadius)/(1.0f-wheelRadius); + } + + float fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward()); + CVector contactPoints[4]; // relative to model + CVector contactSpeeds[4]; // speed at contact points + CVector springDirections[4]; // normalized, in model space + + for(i = 0; i < 4; i++){ + // Set spring under certain circumstances + if(Damage.GetWheelStatus(i) == WHEEL_STATUS_MISSING) + m_aSuspensionSpringRatio[i] = 1.0f; + else if(Damage.GetWheelStatus(i) == WHEEL_STATUS_BURST){ + // wheel more bumpy the faster we are + if(CGeneral::GetRandomNumberInRange(0, (uint16)(40*fwdSpeed) + 98) < 100){ + m_aSuspensionSpringRatio[i] += 0.3f*(m_aSuspensionLineLength[i]-m_aSuspensionSpringLength[i])/m_aSuspensionSpringLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + } + + // get points and directions if spring is compressed + if(m_aSuspensionSpringRatio[i] < 1.0f){ + contactPoints[i] = m_aWheelColPoints[i].point - GetPosition(); + springDirections[i] = Multiply3x3(GetMatrix(), colModel->lines[i].p1 - colModel->lines[i].p0); + springDirections[i].Normalise(); + } + } + + // Make springs push up vehicle + for(i = 0; i < 4; i++){ + if(m_aSuspensionSpringRatio[i] < 1.0f){ + float bias = pHandling->fSuspensionBias; + if(i == 1 || i == 3) // rear + bias = 1.0f - bias; + + ApplySpringCollision(pHandling->fSuspensionForceLevel, + springDirections[i], contactPoints[i], + m_aSuspensionSpringRatio[i], bias); + m_aWheelSkidmarkMuddy[i] = + m_aWheelColPoints[i].surfaceB == SURFACE_GRASS || + m_aWheelColPoints[i].surfaceB == SURFACE_DIRTTRACK || + m_aWheelColPoints[i].surfaceB == SURFACE_SAND; + }else{ + contactPoints[i] = Multiply3x3(GetMatrix(), colModel->lines[i].p1); + } + } + + // Get speed at contact points + for(i = 0; i < 4; i++){ + contactSpeeds[i] = GetSpeed(contactPoints[i]); + if(m_aGroundPhysical[i]){ + // subtract movement of physical we're standing on + contactSpeeds[i] -= m_aGroundPhysical[i]->GetSpeed(m_aGroundOffset[i]); +#ifndef FIX_BUGS + // this shouldn't be reset because we still need it below + m_aGroundPhysical[i] = nil; +#endif + } + } + + // dampen springs + for(i = 0; i < 4; i++) + if(m_aSuspensionSpringRatio[i] < 1.0f) + ApplySpringDampening(pHandling->fSuspensionDampingLevel, + springDirections[i], contactPoints[i], contactSpeeds[i]); + + // Get speed at contact points again + for(i = 0; i < 4; i++){ + contactSpeeds[i] = GetSpeed(contactPoints[i]); + if(m_aGroundPhysical[i]){ + // subtract movement of physical we're standing on + contactSpeeds[i] -= m_aGroundPhysical[i]->GetSpeed(m_aGroundOffset[i]); + m_aGroundPhysical[i] = nil; + } + } + + + bool gripCheat = true; + fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward()); + if(!strongGrip1 && !CVehicle::bCheat3) + gripCheat = false; + float acceleration = pHandling->Transmission.CalculateDriveAcceleration(m_fGasPedal, m_nCurrentGear, m_fChangeGearTime, fwdSpeed, gripCheat); + acceleration /= fForceMultiplier; + + // unused + if(GetModelIndex() == MI_MIAMI_RCBARON || + GetModelIndex() == MI_MIAMI_RCRAIDER || + GetModelIndex() == MI_MIAMI_SPARROW) + acceleration = 0.0f; + + brake = m_fBrakePedal * pHandling->fBrakeDeceleration * CTimer::GetTimeStep(); + bool neutralHandling = !!(pHandling->Flags & HANDLING_NEUTRALHANDLING); + float brakeBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fBrakeBias; + float brakeBiasRear = neutralHandling ? 1.0f : 2.0f*(1.0f-pHandling->fBrakeBias); + float tractionBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fTractionBias; + float tractionBiasRear = neutralHandling ? 1.0f : 2.0f*(1.0f-pHandling->fTractionBias); + + // Count how many wheels are touching the ground + + m_nWheelsOnGround = 0; + m_nDriveWheelsOnGroundPrev = m_nDriveWheelsOnGround; + m_nDriveWheelsOnGround = 0; + + for(i = 0; i < 4; i++){ + if(m_aSuspensionSpringRatio[i] < 1.0f) + m_aWheelTimer[i] = 4.0f; + else + m_aWheelTimer[i] = Max(m_aWheelTimer[i]-CTimer::GetTimeStep(), 0.0f); + + if(m_aWheelTimer[i] > 0.0f){ + m_nWheelsOnGround++; + switch(pHandling->Transmission.nDriveType){ + case '4': + m_nDriveWheelsOnGround++; + break; + case 'F': + if(i == CARWHEEL_FRONT_LEFT || i == CARWHEEL_FRONT_RIGHT) + m_nDriveWheelsOnGround++; + break; + case 'R': + if(i == CARWHEEL_REAR_LEFT || i == CARWHEEL_REAR_RIGHT) + m_nDriveWheelsOnGround++; + break; + } + } + } + + float traction; + if(m_status == STATUS_PHYSICS) + traction = 0.004f * m_fTraction; + else + traction = 0.004f; + traction *= pHandling->fTractionMultiplier / 4.0f; + traction /= fForceMultiplier; + if(CVehicle::bCheat3) + traction *= 4.0f; + + if(FindPlayerVehicle() && FindPlayerVehicle() == this){ + if(CPad::GetPad(0)->WeaponJustDown()){ + if(m_bombType == CARBOMB_TIMED){ + m_bombType = CARBOMB_TIMEDACTIVE; + m_nBombTimer = 7000; + m_pBlowUpEntity = FindPlayerPed(); + CGarages::TriggerMessage("GA_12", -1, 3000, -1); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_BOMB_TIMED_ACTIVATED, 1.0f); + }else if(m_bombType == CARBOMB_ONIGNITION){ + m_bombType = CARBOMB_ONIGNITIONACTIVE; + CGarages::TriggerMessage("GA_12", -1, 3000, -1); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_BOMB_ONIGNITION_ACTIVATED, 1.0f); + } + } + }else if(strongGrip1 || CVehicle::bCheat3){ + traction *= 1.2f; + acceleration *= 1.4f; + if(strongGrip2 || CVehicle::bCheat3){ + traction *= 1.3f; + acceleration *= 1.4f; + } + } + + static float fThrust; + static tWheelState WheelState[4]; + + // Process front wheels on ground + + if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] > 0.0f || m_aWheelTimer[CARWHEEL_FRONT_RIGHT] > 0.0f){ + float s = Sin(m_fSteerAngle); + float c = Cos(m_fSteerAngle); + CVector wheelFwd = Multiply3x3(GetMatrix(), CVector(-s, c, 0.0f)); + CVector wheelRight = Multiply3x3(GetMatrix(), CVector(c, s, 0.0f)); + + if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] > 0.0f){ + if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier)) + fThrust = 0.0f; + else + fThrust = acceleration; + + m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceA = SURFACE_RUBBER29; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_FRONT_LEFT])*traction; + if(m_status == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceB); + WheelState[CARWHEEL_FRONT_LEFT] = m_aWheelState[CARWHEEL_FRONT_LEFT]; + + if(Damage.GetWheelStatus(VEHWHEEL_FRONT_LEFT) == WHEEL_STATUS_BURST) + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_LEFT], contactPoints[CARWHEEL_FRONT_LEFT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront*Damage.m_fWheelDamageEffect, + CARWHEEL_FRONT_LEFT, + &m_aWheelSpeed[CARWHEEL_FRONT_LEFT], + &WheelState[CARWHEEL_FRONT_LEFT], + WHEEL_STATUS_BURST); + else + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_LEFT], contactPoints[CARWHEEL_FRONT_LEFT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront, + CARWHEEL_FRONT_LEFT, + &m_aWheelSpeed[CARWHEEL_FRONT_LEFT], + &WheelState[CARWHEEL_FRONT_LEFT], + WHEEL_STATUS_OK); + } + + if(m_aWheelTimer[CARWHEEL_FRONT_RIGHT] > 0.0f){ + if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier)) + fThrust = 0.0f; + else + fThrust = acceleration; + + m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceA = SURFACE_RUBBER29; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_FRONT_RIGHT])*traction; + if(m_status == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceB); + WheelState[CARWHEEL_FRONT_RIGHT] = m_aWheelState[CARWHEEL_FRONT_RIGHT]; + + if(Damage.GetWheelStatus(VEHWHEEL_FRONT_RIGHT) == WHEEL_STATUS_BURST) + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_RIGHT], contactPoints[CARWHEEL_FRONT_RIGHT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront*Damage.m_fWheelDamageEffect, + CARWHEEL_FRONT_RIGHT, + &m_aWheelSpeed[CARWHEEL_FRONT_RIGHT], + &WheelState[CARWHEEL_FRONT_RIGHT], + WHEEL_STATUS_BURST); + else + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_RIGHT], contactPoints[CARWHEEL_FRONT_RIGHT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront, + CARWHEEL_FRONT_RIGHT, + &m_aWheelSpeed[CARWHEEL_FRONT_RIGHT], + &WheelState[CARWHEEL_FRONT_RIGHT], + WHEEL_STATUS_OK); + } + } + + // Process front wheels off ground + + if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] <= 0.0f){ + if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] *= 0.95f; + else{ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_FRONT_LEFT] < 2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_FRONT_LEFT] > -2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] += 0.1f; + } + } + m_aWheelRotation[CARWHEEL_FRONT_LEFT] += m_aWheelSpeed[CARWHEEL_FRONT_LEFT]; + } + if(m_aWheelTimer[CARWHEEL_FRONT_RIGHT] <= 0.0f){ + if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] *= 0.95f; + else{ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] < 2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] > -2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] += 0.1f; + } + } + m_aWheelRotation[CARWHEEL_FRONT_RIGHT] += m_aWheelSpeed[CARWHEEL_FRONT_RIGHT]; + } + + // Process rear wheels + + if(m_aWheelTimer[CARWHEEL_REAR_LEFT] > 0.0f || m_aWheelTimer[CARWHEEL_REAR_RIGHT] > 0.0f){ + CVector wheelFwd = GetForward(); + CVector wheelRight = GetRight(); + + if(bIsHandbrakeOn) + brake = 20000.0f; + + if(m_aWheelTimer[CARWHEEL_REAR_LEFT] > 0.0f){ + if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier)) + fThrust = 0.0f; + else + fThrust = acceleration; + + m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceA = SURFACE_RUBBER29; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_REAR_LEFT])*traction; + if(m_status == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceB); + WheelState[CARWHEEL_REAR_LEFT] = m_aWheelState[CARWHEEL_REAR_LEFT]; + + if(Damage.GetWheelStatus(VEHWHEEL_REAR_LEFT) == WHEEL_STATUS_BURST) + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_REAR_LEFT], contactPoints[CARWHEEL_REAR_LEFT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasRear, + adhesion*tractionBiasRear*Damage.m_fWheelDamageEffect, + CARWHEEL_REAR_LEFT, + &m_aWheelSpeed[CARWHEEL_REAR_LEFT], + &WheelState[CARWHEEL_REAR_LEFT], + WHEEL_STATUS_BURST); + else + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_REAR_LEFT], contactPoints[CARWHEEL_REAR_LEFT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasRear, + adhesion*tractionBiasRear, + CARWHEEL_REAR_LEFT, + &m_aWheelSpeed[CARWHEEL_REAR_LEFT], + &WheelState[CARWHEEL_REAR_LEFT], + WHEEL_STATUS_OK); + } + + if(m_aWheelTimer[CARWHEEL_REAR_RIGHT] > 0.0f){ + if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier)) + fThrust = 0.0f; + else + fThrust = acceleration; + + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceA = SURFACE_RUBBER29; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_REAR_RIGHT])*traction; + if(m_status == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceB); + WheelState[CARWHEEL_REAR_RIGHT] = m_aWheelState[CARWHEEL_REAR_RIGHT]; + + if(Damage.GetWheelStatus(VEHWHEEL_REAR_RIGHT) == WHEEL_STATUS_BURST) + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_REAR_RIGHT], contactPoints[CARWHEEL_REAR_RIGHT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasRear, + adhesion*tractionBiasRear*Damage.m_fWheelDamageEffect, + CARWHEEL_REAR_RIGHT, + &m_aWheelSpeed[CARWHEEL_REAR_RIGHT], + &WheelState[CARWHEEL_REAR_RIGHT], + WHEEL_STATUS_BURST); + else + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_REAR_RIGHT], contactPoints[CARWHEEL_REAR_RIGHT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasRear, + adhesion*tractionBiasRear, + CARWHEEL_REAR_RIGHT, + &m_aWheelSpeed[CARWHEEL_REAR_RIGHT], + &WheelState[CARWHEEL_REAR_RIGHT], + WHEEL_STATUS_OK); + } + } + + // Process rear wheels off ground + + if(m_aWheelTimer[CARWHEEL_REAR_LEFT] <= 0.0f){ + if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) + m_aWheelSpeed[CARWHEEL_REAR_LEFT] *= 0.95f; + else{ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_REAR_LEFT] < 2.0f) + m_aWheelSpeed[CARWHEEL_REAR_LEFT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_REAR_LEFT] > -2.0f) + m_aWheelSpeed[CARWHEEL_REAR_LEFT] += 0.1f; + } + } + m_aWheelRotation[CARWHEEL_REAR_LEFT] += m_aWheelSpeed[CARWHEEL_REAR_LEFT]; + } + if(m_aWheelTimer[CARWHEEL_REAR_RIGHT] <= 0.0f){ + if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) || acceleration == 0.0f) + m_aWheelSpeed[CARWHEEL_REAR_RIGHT] *= 0.95f; + else{ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_REAR_RIGHT] < 2.0f) + m_aWheelSpeed[CARWHEEL_REAR_RIGHT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_REAR_RIGHT] > -2.0f) + m_aWheelSpeed[CARWHEEL_REAR_RIGHT] += 0.1f; + } + } + m_aWheelRotation[CARWHEEL_REAR_RIGHT] += m_aWheelSpeed[CARWHEEL_REAR_RIGHT]; + } + + for(i = 0; i < 4; i++){ + float wheelPos = colModel->lines[i].p0.z; + if(m_aSuspensionSpringRatio[i] > 0.0f) + wheelPos -= m_aSuspensionSpringRatio[i]*m_aSuspensionSpringLength[i]; + m_aWheelPosition[i] += (wheelPos - m_aWheelPosition[i])*0.75f; + } + for(i = 0; i < 4; i++) + m_aWheelState[i] = WheelState[i]; + + // Process horn + + if(m_status != STATUS_PLAYER){ + ReduceHornCounter(); + }else{ + if(GetModelIndex() == MI_MRWHOOP){ + if(Pads[0].bHornHistory[Pads[0].iCurrHornHistory] && + !Pads[0].bHornHistory[(Pads[0].iCurrHornHistory+4) % 5]){ + m_bSirenOrAlarm = !m_bSirenOrAlarm; + printf("m_bSirenOrAlarm toggled to %d\n", m_bSirenOrAlarm); + } + }else if(UsesSiren(GetModelIndex())){ + if(Pads[0].bHornHistory[Pads[0].iCurrHornHistory]){ + if(Pads[0].bHornHistory[(Pads[0].iCurrHornHistory+4) % 5] && + Pads[0].bHornHistory[(Pads[0].iCurrHornHistory+3) % 5]) + m_nCarHornTimer = 1; + else + m_nCarHornTimer = 0; + }else if(Pads[0].bHornHistory[(Pads[0].iCurrHornHistory+4) % 5] && + !Pads[0].bHornHistory[(Pads[0].iCurrHornHistory+1) % 5]){ + m_nCarHornTimer = 0; + m_bSirenOrAlarm = !m_bSirenOrAlarm; + }else + m_nCarHornTimer = 0; + }else if(GetModelIndex() != MI_YARDIE && !CVehicle::bCheat3){ + if(Pads[0].GetHorn()) + m_nCarHornTimer = 1; + else + m_nCarHornTimer = 0; + } + } + + // Flying + + if(m_status != STATUS_PLAYER && m_status != STATUS_PLAYER_REMOTE && m_status != STATUS_PHYSICS){ + if(GetModelIndex() == MI_MIAMI_RCRAIDER || GetModelIndex() == MI_MIAMI_SPARROW) + m_aWheelSpeed[0] = Max(m_aWheelSpeed[0]-0.0005f, 0.0f); + }else if((GetModelIndex() == MI_DODO || CVehicle::bAllDodosCheat) && + m_vecMoveSpeed.Magnitude() > 0.0f && CTimer::GetTimeStep() > 0.0f){ + FlyingControl(FLIGHT_MODEL_DODO); + }else if(GetModelIndex() == MI_MIAMI_RCBARON){ + FlyingControl(FLIGHT_MODEL_HELI); + }else if(GetModelIndex() == MI_MIAMI_RCRAIDER || GetModelIndex() == MI_MIAMI_SPARROW || bAllCarCheat){ + if(CPad::GetPad(0)->GetCircleJustDown()) + m_aWheelSpeed[0] = Max(m_aWheelSpeed[0]-0.03f, 0.0f); + if(m_aWheelSpeed[0] < 0.22f) + m_aWheelSpeed[0] += 0.0001f; + if(m_aWheelSpeed[0] > 0.15f) + FlyingControl(FLIGHT_MODEL_HELI); + } + } + + + + // Process car on fire + // A similar calculation of damagePos is done elsewhere for smoke + + uint8 engineStatus = Damage.GetEngineStatus(); + CVector damagePos = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->m_positions[CAR_POS_HEADLIGHTS]; + + switch(Damage.GetDoorStatus(DOOR_BONNET)){ + case DOOR_STATUS_OK: + case DOOR_STATUS_SMASHED: + // Bonnet is still there, smoke comes out at the edge + damagePos += vecDAMAGE_ENGINE_POS_SMALL; + break; + case DOOR_STATUS_SWINGING: + case DOOR_STATUS_MISSING: + // Bonnet is gone, smoke comes out at the engine + damagePos += vecDAMAGE_ENGINE_POS_BIG; + break; + } + + // move fire forward if in first person + if(this == FindPlayerVehicle() && TheCamera.GetLookingForwardFirstPerson()) + if(m_fHealth < 250.0f && m_status != STATUS_WRECKED){ + if(GetModelIndex() == MI_FIRETRUCK) + damagePos += CVector(0.0f, 3.0f, -0.2f); + else + damagePos += CVector(0.0f, 1.2f, -0.8f); + } + + damagePos = GetMatrix()*damagePos; + damagePos.z += 0.15f; + + if(m_fHealth < 250.0f && m_status != STATUS_WRECKED){ + // Car is on fire + + CParticle::AddParticle(PARTICLE_CARFLAME, damagePos, + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.01125f, 0.09f)), + nil, 0.9f); + + CVector coors = damagePos; + coors.x += CGeneral::GetRandomNumberInRange(-0.5625f, 0.5625f), + coors.y += CGeneral::GetRandomNumberInRange(-0.5625f, 0.5625f), + coors.z += CGeneral::GetRandomNumberInRange(0.5625f, 2.25f); + CParticle::AddParticle(PARTICLE_CARFLAME_SMOKE, coors, CVector(0.0f, 0.0f, 0.0f)); + + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, damagePos, CVector(0.0f, 0.0f, 0.0f), nil, 0.5f); + + // Blow up car after 5 seconds + m_fFireBlowUpTimer += CTimer::GetTimeStepInMilliseconds(); + if(m_fFireBlowUpTimer > 5000.0f){ + CWorld::Players[CWorld::PlayerInFocus].AwardMoneyForExplosion(this); + BlowUpCar(m_pSetOnFireEntity); + } + }else + m_fFireBlowUpTimer = 0.0f; + + // Decrease car health if engine is damaged badly + if(engineStatus > ENGINE_STATUS_ON_FIRE && m_fHealth > 250.0f) + m_fHealth -= 2.0f; + + ProcessDelayedExplosion(); + + + if(m_bSirenOrAlarm && (CTimer::GetFrameCounter()&7) == 5 && + UsesSiren(GetModelIndex()) && GetModelIndex() != MI_RCBANDIT) + CCarAI::MakeWayForCarWithSiren(this); + + + // Find out how much to shake the pad depending on suspension and ground surface + + float suspShake = 0.0f; + float surfShake = 0.0f; + for(i = 0; i < 4; i++){ + float suspChange = m_aSuspensionSpringRatioPrev[i] - m_aSuspensionSpringRatio[i]; + if(suspChange > 0.3f){ + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP, suspChange); + if(suspChange > suspShake) + suspShake = suspChange; + } + + uint8 surf = m_aWheelColPoints[i].surfaceB; + if(surf == SURFACE_DIRT || surf == SURFACE_PUDDLE || surf == SURFACE_HEDGE){ + if(surfShake < 0.2f) + surfShake = 0.3f; + }else if(surf == SURFACE_DIRTTRACK || surf == SURFACE_SAND){ + if(surfShake < 0.1f) + surfShake = 0.2f; + }else if(surf == SURFACE_GRASS){ + if(surfShake < 0.05f) + surfShake = 0.1f; + } + + m_aSuspensionSpringRatioPrev[i] = m_aSuspensionSpringRatio[i]; + m_aSuspensionSpringRatio[i] = 1.0f; + } + + // Shake pad + + if((suspShake > 0.0f || surfShake > 0.0f) && m_status == STATUS_PLAYER){ + float speed = m_vecMoveSpeed.MagnitudeSqr(); + if(speed > sq(0.1f)){ + speed = Sqrt(speed); + if(suspShake > 0.0f){ + uint8 freq = Min(200.0f*suspShake*speed*2000.0f/m_fMass + 100.0f, 250.0f); + CPad::GetPad(0)->StartShake(20000.0f*CTimer::GetTimeStep()/freq, freq); + }else{ + uint8 freq = Min(200.0f*surfShake*speed*2000.0f/m_fMass + 40.0f, 145.0f); + CPad::GetPad(0)->StartShake(5000.0f*CTimer::GetTimeStep()/freq, freq); + } + } + } + + bVehicleColProcessed = false; + + if(!bWarnedPeds) + CCarCtrl::ScanForPedDanger(this); + + + // Turn around at the edge of the world + // TODO: make the numbers defines + + float heading; + if(GetPosition().x > 1900.0f){ + if(m_vecMoveSpeed.x > 0.0f) + m_vecMoveSpeed.x *= -1.0f; + heading = GetForward().Heading(); + if(heading > 0.0f) // going west + SetHeading(-heading); + }else if(GetPosition().x < -1900.0f){ + if(m_vecMoveSpeed.x < 0.0f) + m_vecMoveSpeed.x *= -1.0f; + heading = GetForward().Heading(); + if(heading < 0.0f) // going east + SetHeading(-heading); + } + if(GetPosition().y > 1900.0f){ + if(m_vecMoveSpeed.y > 0.0f) + m_vecMoveSpeed.y *= -1.0f; + heading = GetForward().Heading(); + if(heading < HALFPI && heading > 0.0f) + SetHeading(PI-heading); + else if(heading > -HALFPI && heading < 0.0f) + SetHeading(-PI-heading); + }else if(GetPosition().y < -1900.0f){ + if(m_vecMoveSpeed.y < 0.0f) + m_vecMoveSpeed.y *= -1.0f; + heading = GetForward().Heading(); + if(heading > HALFPI) + SetHeading(PI-heading); + else if(heading < -HALFPI) + SetHeading(-PI-heading); + } + + if(bInfiniteMass){ + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); + }else if(!skipPhysics && + (m_fGasPedal == 0.0f && brake == 0.0f || m_status == STATUS_WRECKED)){ + if(Abs(m_vecMoveSpeed.x) < 0.005f && + Abs(m_vecMoveSpeed.y) < 0.005f && + Abs(m_vecMoveSpeed.z) < 0.005f){ + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnSpeed.z = 0.0f; + } + } +} + +void +CAutomobile::Teleport(CVector pos) +{ + CWorld::Remove(this); + + GetPosition() = pos; + SetOrientation(0.0f, 0.0f, 0.0f); + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + + ResetSuspension(); + + CWorld::Add(this); +} + +void +CAutomobile::PreRender(void) +{ + int i, j, n; + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + if(GetModelIndex() == MI_RCBANDIT){ + CVector pos = GetMatrix() * CVector(0.218f, -0.444f, 0.391f); + CAntennas::RegisterOne((uintptr)this, GetUp(), pos, 1.0f); + } + + float fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward())*180.0f; + + + // Wheel particles + + if(GetModelIndex() == MI_DODO){ + ; // nothing + }else if(GetModelIndex() == MI_RCBANDIT){ + for(i = 0; i < 4; i++){ + // Game has same code three times here + switch(m_aWheelState[i]){ + case WHEEL_STATE_SPINNING: + case WHEEL_STATE_SKIDDING: + case WHEEL_STATE_FIXED: + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.05f), + CVector(0.0f, 0.0f, 0.0f), nil, 0.1f); + break; + } + } + }else{ + if(m_status == STATUS_SIMPLE){ + CMatrix mat; + CVector pos; + + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RB])); + pos = mat.GetPosition(); + pos.z = 1.5f*m_aWheelPosition[CARWHEEL_REAR_RIGHT]; + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point = GetMatrix() * pos; + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceB = SURFACE_DEFAULT; + + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LB])); + pos = mat.GetPosition(); + pos.z = 1.5f*m_aWheelPosition[CARWHEEL_REAR_LEFT]; + m_aWheelColPoints[CARWHEEL_REAR_LEFT].point = GetMatrix() * pos; + m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceB = SURFACE_DEFAULT; + + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); + pos = mat.GetPosition(); + pos.z = 1.5f*m_aWheelPosition[CARWHEEL_FRONT_RIGHT]; + m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].point = GetMatrix() * pos; + m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceB = SURFACE_DEFAULT; + + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF])); + pos = mat.GetPosition(); + pos.z = 1.5f*m_aWheelPosition[CARWHEEL_FRONT_LEFT]; + m_aWheelColPoints[CARWHEEL_FRONT_LEFT].point = GetMatrix() * pos; + m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceB = SURFACE_DEFAULT; + } + + int drawParticles = Abs(fwdSpeed) < 90.0f; + if(m_status == STATUS_SIMPLE || m_status == STATUS_PHYSICS || + m_status == STATUS_PLAYER || m_status == STATUS_PLAYER_PLAYBACKFROMBUFFER){ + bool rearSkidding = false; + if(m_aWheelState[CARWHEEL_REAR_LEFT] == WHEEL_STATE_SKIDDING || + m_aWheelState[CARWHEEL_REAR_RIGHT] == WHEEL_STATE_SKIDDING) + rearSkidding = true; + + for(i = 0; i < 4; i++){ + switch(m_aWheelState[i]){ + case WHEEL_STATE_SPINNING: + if(AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles)){ + CParticle::AddParticle(PARTICLE_BURNINGRUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.0f)); + + CParticle::AddParticle(PARTICLE_BURNINGRUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.05f)); + } + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.0f)); + + if(m_aWheelTimer[i] > 0.0f) + CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point, + GetForward().x, GetForward().y, + &m_aWheelSkidmarkMuddy[i], &m_aWheelSkidmarkBloody[i]); + break; + + case WHEEL_STATE_SKIDDING: + if(i == CARWHEEL_REAR_LEFT || i == CARWHEEL_REAR_RIGHT || rearSkidding){ + // same as below + + AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.0f)); + + if(m_aWheelTimer[i] > 0.0f) + CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point, + GetForward().x, GetForward().y, + &m_aWheelSkidmarkMuddy[i], &m_aWheelSkidmarkBloody[i]); + } + break; + + case WHEEL_STATE_FIXED: + AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.0f)); + + if(m_aWheelTimer[i] > 0.0f) + CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point, + GetForward().x, GetForward().y, + &m_aWheelSkidmarkMuddy[i], &m_aWheelSkidmarkBloody[i]); + break; + + default: + if(Abs(fwdSpeed) > 0.5f) + AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles); + if(m_aWheelSkidmarkBloody[i] && m_aWheelTimer[i] > 0.0f) + CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point, + GetForward().x, GetForward().y, + &m_aWheelSkidmarkMuddy[i], &m_aWheelSkidmarkBloody[i]); + } + } + } + } + + if(m_aCarNodes[CAR_WHEEL_RM]){ + // assume middle wheels are two units before rear ones + CVector offset = GetForward()*2.0f; + + switch(m_aWheelState[CARWHEEL_REAR_LEFT]){ + // Game has same code three times here + case WHEEL_STATE_SPINNING: + case WHEEL_STATE_SKIDDING: + case WHEEL_STATE_FIXED: + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[CARWHEEL_REAR_LEFT].point + CVector(0.0f, 0.0f, 0.25f) + offset, + CVector(0.0f, 0.0f, 0.0f)); + + if(m_aWheelTimer[CARWHEEL_REAR_LEFT] > 0.0f) + CSkidmarks::RegisterOne((uintptr)this + CARWHEEL_REAR_LEFT, + m_aWheelColPoints[CARWHEEL_REAR_LEFT].point + offset, + GetForward().x, GetForward().y, + &m_aWheelSkidmarkMuddy[CARWHEEL_REAR_LEFT], &m_aWheelSkidmarkBloody[CARWHEEL_REAR_LEFT]); + break; + } + + switch(m_aWheelState[CARWHEEL_REAR_RIGHT]){ + // Game has same code three times here + case WHEEL_STATE_SPINNING: + case WHEEL_STATE_SKIDDING: + case WHEEL_STATE_FIXED: + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point + CVector(0.0f, 0.0f, 0.25f) + offset, + CVector(0.0f, 0.0f, 0.0f)); + + if(m_aWheelTimer[CARWHEEL_REAR_RIGHT] > 0.0f) + CSkidmarks::RegisterOne((uintptr)this + CARWHEEL_REAR_RIGHT, + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point + offset, + GetForward().x, GetForward().y, + &m_aWheelSkidmarkMuddy[CARWHEEL_REAR_RIGHT], &m_aWheelSkidmarkBloody[CARWHEEL_REAR_RIGHT]); + break; + } + } + + + // Rain on roof + if(!CCullZones::CamNoRain() && !CCullZones::PlayerNoRain() && + Abs(fwdSpeed) < 20.0f && CWeather::Rain > 0.02f){ + CColModel *colModel = GetColModel(); + + for(i = 0; i < colModel->numTriangles; i++){ + CVector p1, p2, p3, c; + + colModel->GetTrianglePoint(p1, colModel->triangles[i].a); + p1 = GetMatrix() * p1; + colModel->GetTrianglePoint(p2, colModel->triangles[i].b); + p2 = GetMatrix() * p2; + colModel->GetTrianglePoint(p3, colModel->triangles[i].c); + p3 = GetMatrix() * p3; + c = (p1 + p2 + p3)/3.0f; + + n = 6.0f*CWeather::Rain; + for(j = 0; j <= n; j++) + CParticle::AddParticle(PARTICLE_RAIN_SPLASHUP, + c + CVector(CGeneral::GetRandomNumberInRange(-.04f, 0.4f), CGeneral::GetRandomNumberInRange(-.04f, 0.4f), 0.0f), + CVector(0.0f, 0.0f, 0.0f), + nil, 0.0f, 0, 0, CGeneral::GetRandomNumber() & 1); + } + } + + AddDamagedVehicleParticles(); + + // Exhaust smoke + if(bEngineOn && fwdSpeed < 90.0f){ + CVector exhaustPos = mi->m_positions[CAR_POS_EXHAUST]; + CVector pos1, pos2, dir; + + if(exhaustPos != CVector(0.0f, 0.0f, 0.0f)){ + dir.z = 0.0f; + if(fwdSpeed < 10.0f){ + CVector steerFwd(-Sin(m_fSteerAngle), Cos(m_fSteerAngle), 0.0f); + steerFwd = Multiply3x3(GetMatrix(), steerFwd); + float r = CGeneral::GetRandomNumberInRange(-0.06f, -0.03f); + dir.x = steerFwd.x * r; + dir.y = steerFwd.y * r; + }else{ + dir.x = m_vecMoveSpeed.x; + dir.y = m_vecMoveSpeed.y; + } + + bool dblExhaust = false; + pos1 = GetMatrix() * exhaustPos; + if(pHandling->Flags & HANDLING_DBL_EXHAUST){ + dblExhaust = true; + pos2 = exhaustPos; + pos2.x = -pos2.x; + pos2 = GetMatrix() * pos2; + } + + n = 4.0f*m_fGasPedal; + if(dblExhaust) + for(i = 0; i <= n; i++){ + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir); + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos2, dir); + } + else + for(i = 0; i <= n; i++) + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir); + } + } + + + // Siren and taxi lights + switch(GetModelIndex()){ + case MI_FIRETRUCK: + case MI_AMBULAN: + case MI_POLICE: + case MI_ENFORCER: + if(m_bSirenOrAlarm){ + CVector pos1, pos2; + uint8 r1, g1, b1; + uint8 r2, g2, b2; + uint8 r, g, b; + + switch(GetModelIndex()){ + case MI_FIRETRUCK: + pos1 = CVector(1.1f, 1.7f, 2.0f); + pos2 = CVector(-1.1f, 1.7f, 2.0f); + r1 = 255; g1 = 0; b1 = 0; + r2 = 255; g2 = 255; b2 = 0; + break; + case MI_AMBULAN: + pos1 = CVector(1.1f, 0.9f, 1.6f); + pos2 = CVector(-1.1f, 0.9f, 1.6f); + r1 = 255; g1 = 0; b1 = 0; + r2 = 255; g2 = 255; b2 = 255; + break; + case MI_POLICE: + pos1 = CVector(0.7f, -0.4f, 1.0f); + pos2 = CVector(-0.7f, -0.4f, 1.0f); + r1 = 255; g1 = 0; b1 = 0; + r2 = 0; g2 = 0; b2 = 255; + break; + case MI_ENFORCER: + pos1 = CVector(1.1f, 0.8f, 1.2f); + pos2 = CVector(-1.1f, 0.8f, 1.2f); + r1 = 255; g1 = 0; b1 = 0; + r2 = 0; g2 = 0; b2 = 255; + break; + } + + uint32 t = CTimer::GetTimeInMilliseconds() & 0x3FF; // 1023 + if(t < 512){ + r = r1/6; + g = g1/6; + b = b1/6; + }else{ + r = r2/6; + g = g2/6; + b = b2/6; + } + + t = CTimer::GetTimeInMilliseconds() & 0x1FF; // 511 + if(t < 100){ + float f = t/100.0f; + r *= f; + g *= f; + b *= f; + }else if(t > 412){ + float f = (512-t)/100.0f; + r *= f; + g *= f; + b *= f; + } + + CVector pos = GetPosition(); + float angle = (CTimer::GetTimeInMilliseconds() & 0x3FF)*TWOPI/0x3FF; + float s = 8.0f*Sin(angle); + float c = 8.0f*Cos(angle); + CShadows::StoreCarLightShadow(this, (uintptr)this + 21, gpShadowHeadLightsTex, + &pos, c, s, s, -c, r, g, b, 8.0f); + + CPointLights::AddLight(CPointLights::LIGHT_POINT, + pos + GetUp()*2.0f, CVector(0.0f, 0.0f, 0.0f), 12.0f, + r*0.02f, g*0.02f, b*0.02f, CPointLights::FOG_NONE, true); + + pos1 = GetMatrix() * pos1; + pos2 = GetMatrix() * pos2; + + for(i = 0; i < 4; i++){ + uint8 sirenTimer = ((CTimer::GetTimeInMilliseconds() + (i<<6))>>8) & 3; + pos = (pos1*i + pos2*(3.0f-i))/3.0f; + + switch(sirenTimer){ + case 0: + CCoronas::RegisterCorona((uintptr)this + 21 + i, + r1, g1, b1, 255, + pos, 0.4f, 50.0f, + CCoronas::TYPE_STAR, + i == 1 ? CCoronas::FLARE_HEADLIGHTS : CCoronas::FLARE_NONE, + CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + break; + case 2: + CCoronas::RegisterCorona((uintptr)this + 21 + i, + r2, g2, b2, 255, + pos, 0.4f, 50.0f, + CCoronas::TYPE_STAR, + CCoronas::FLARE_NONE, + CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + break; + default: + CCoronas::UpdateCoronaCoors((uintptr)this + 21 + i, pos, 50.0f, 0.0f); + break; + } + } + } + break; + + case MI_FBICAR: + if(m_bSirenOrAlarm){ + CVector pos = GetMatrix() * CVector(0.4f, 0.6f, 0.3f); + if(CTimer::GetTimeInMilliseconds() & 0x100 && + DotProduct(GetForward(), GetPosition() - TheCamera.GetPosition()) < 0.0f) + CCoronas::RegisterCorona((uintptr)this + 21, + 0, 0, 255, 255, + pos, 0.4f, 50.0f, + CCoronas::TYPE_STAR, + CCoronas::FLARE_NONE, + CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + else + CCoronas::UpdateCoronaCoors((uintptr)this + 21, pos, 50.0f, 0.0f); + } + break; + + case MI_TAXI: + case MI_CABBIE: + case MI_BORGNINE: + if(bTaxiLight){ + CVector pos = GetPosition() + GetUp()*0.95f; + CCoronas::RegisterCorona((uintptr)this + 21, + 128, 128, 0, 255, + pos, 0.8f, 50.0f, + CCoronas::TYPE_NORMAL, + CCoronas::FLARE_NONE, + CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + CPointLights::AddLight(CPointLights::LIGHT_POINT, + pos, CVector(0.0f, 0.0f, 0.0f), 10.0f, + 1.0f, 1.0f, 0.5f, CPointLights::FOG_NONE, true); + } + break; + } + + if(GetModelIndex() == MI_RCBANDIT || + GetModelIndex() == MI_DODO || + GetModelIndex() == MI_RHINO) + goto nolights; + + // Turn lights on/off + bool shouldLightsBeOn = + CClock::GetHours() > 20 || + CClock::GetHours() > 19 && CClock::GetMinutes() > (m_randomSeed & 0x3F) || + CClock::GetHours() < 7 || + CClock::GetHours() < 8 && CClock::GetMinutes() < (m_randomSeed & 0x3F) || + m_randomSeed/50000.0f < CWeather::Foggyness || + m_randomSeed/50000.0f < CWeather::WetRoads; + if(shouldLightsBeOn != bLightsOn && m_status != STATUS_WRECKED){ + if(m_status == STATUS_ABANDONED){ + // Turn off lights on abandoned vehicles only when we they're far away + if(bLightsOn && + Abs(TheCamera.GetPosition().x - GetPosition().x) + Abs(TheCamera.GetPosition().y - GetPosition().y) > 100.0f) + bLightsOn = false; + }else + bLightsOn = shouldLightsBeOn; + } + + // Actually render the lights + bool alarmOn = false; + bool alarmOff = false; + if(IsAlarmOn()){ + if(CTimer::GetTimeInMilliseconds() & 0x100) + alarmOn = true; + else + alarmOff = true; + } + if(bEngineOn && bLightsOn || alarmOn || alarmOff){ + CVector lookVector = GetPosition() - TheCamera.GetPosition(); + float camDist = lookVector.Magnitude(); + if(camDist != 0.0f) + lookVector *= 1.0f/camDist; + else + lookVector = CVector(1.0f, 0.0f, 0.0f); + + // 1.0 if directly behind car, -1.0 if in front + // BUG on PC: Abs of DotProduct is taken + float behindness = DotProduct(lookVector, GetForward()); + behindness = clamp(behindness, -1.0f, 1.0f); // shouldn't be necessary + // 0.0 if behind car, PI if in front + // Abs not necessary + float angle = Abs(Acos(behindness)); + + // Headlights + + CVector headLightPos = mi->m_positions[CAR_POS_HEADLIGHTS]; + CVector lightR = GetMatrix() * headLightPos; + CVector lightL = lightR; + lightL -= GetRight()*2.0f*headLightPos.x; + + // Headlight coronas + if(behindness < 0.0f){ + // In front of car + float intensity = -0.5f*behindness + 0.3f; + float size = 1.0f - behindness; + + if(behindness < -0.97f && camDist < 30.0f){ + // Directly in front and not too far away + if(pHandling->Flags & HANDLING_HALOGEN_LIGHTS){ + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 6, 150, 150, 195, 255, + lightL, 1.2f, 45.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 7, 150, 150, 195, 255, + lightR, 1.2f, 45.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle); + }else{ + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 6, 160, 160, 140, 255, + lightL, 1.2f, 45.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 7, 160, 160, 140, 255, + lightR, 1.2f, 45.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle); + } + } + + if(alarmOff){ + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this, 0, 0, 0, 0, + lightL, size, 0.0f, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 1, 0, 0, 0, 0, + lightR, size, 0.0f, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + }else{ + if(pHandling->Flags & HANDLING_HALOGEN_LIGHTS){ + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this, 190*intensity, 190*intensity, 255*intensity, 255, + lightL, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 1, 190*intensity, 190*intensity, 255*intensity, 255, + lightR, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + }else{ + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this, 210*intensity, 210*intensity, 195*intensity, 255, + lightL, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 1, 210*intensity, 210*intensity, 195*intensity, 255, + lightR, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + } + } + }else{ + // Behind car + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this, lightL, 50.0f*TheCamera.LODDistMultiplier, angle); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 1, lightR, 50.0f*TheCamera.LODDistMultiplier, angle); + } + + // bright lights + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK && !bNoBrightHeadLights) + CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->FrontLights + 4); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK && !bNoBrightHeadLights) + CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->FrontLights + 4); + + // Taillights + + CVector tailLightPos = mi->m_positions[CAR_POS_TAILLIGHTS]; + lightR = GetMatrix() * tailLightPos; + lightL = lightR; + lightL -= GetRight()*2.0f*tailLightPos.x; + + // Taillight coronas + if(behindness > 0.0f){ + // Behind car + float intensity = 0.4f*behindness + 0.4; + float size = (behindness + 1.0f)/2.0f; + + if(m_fGasPedal < 0.0f){ + // reversing + intensity += 0.4f; + size += 0.3f; + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 2, 128*intensity, 128*intensity, 128*intensity, 255, + lightL, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 3, 128*intensity, 128*intensity, 128*intensity, 255, + lightR, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + }else{ + if(m_fBrakePedal > 0.0){ + intensity += 0.4f; + size += 0.3f; + } + + if(alarmOff){ + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 2, 0, 0, 0, 0, + lightL, size, 0.0f, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 3, 0, 0, 0, 0, + lightR, size, 0.0f, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + }else{ + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 2, 128*intensity, 0, 0, 255, + lightL, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 3, 128*intensity, 0, 0, 255, + lightR, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + } + } + }else{ + // In front of car + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 2, lightL, 50.0f*TheCamera.LODDistMultiplier, angle); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 3, lightR, 50.0f*TheCamera.LODDistMultiplier, angle); + } + + // bright lights + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 8); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 8); + + // Light shadows + if(!alarmOff){ + CVector pos = GetPosition(); + CVector2D fwd(GetForward()); + fwd.Normalise(); + float f = headLightPos.y + 6.0f; + pos += CVector(f*fwd.x, f*fwd.y, 2.0f); + + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK || + Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CShadows::StoreCarLightShadow(this, (uintptr)this + 22, gpShadowHeadLightsTex, &pos, + 7.0f*fwd.x, 7.0f*fwd.y, 7.0f*fwd.y, -7.0f*fwd.x, 45, 45, 45, 7.0f); + + f = (tailLightPos.y - 2.5f) - (headLightPos.y + 6.0f); + pos += CVector(f*fwd.x, f*fwd.y, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK || + Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CShadows::StoreCarLightShadow(this, (uintptr)this + 25, gpShadowExplosionTex, &pos, + 3.0f, 0.0f, 0.0f, -3.0f, 35, 0, 0, 4.0f); + } + + if(this == FindPlayerVehicle() && !alarmOff){ + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK || + Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CPointLights::AddLight(CPointLights::LIGHT_DIRECTIONAL, GetPosition(), GetForward(), + 20.0f, 1.0f, 1.0f, 1.0f, + FindPlayerVehicle()->m_vecMoveSpeed.MagnitudeSqr2D() < sq(0.45f) ? CPointLights::FOG_NORMAL : CPointLights::FOG_NONE, + false); + CVector pos = GetPosition() - 4.0f*GetForward(); + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK || + Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + if(m_fBrakePedal > 0.0f) + CPointLights::AddLight(CPointLights::LIGHT_POINT, pos, CVector(0.0f, 0.0f, 0.0f), + 10.0f, 1.0f, 0.0f, 0.0f, + CPointLights::FOG_NONE, false); + else + CPointLights::AddLight(CPointLights::LIGHT_POINT, pos, CVector(0.0f, 0.0f, 0.0f), + 7.0f, 0.6f, 0.0f, 0.0f, + CPointLights::FOG_NONE, false); + } + }else{ + // Lights off + + if(m_status == STATUS_ABANDONED || m_status == STATUS_WRECKED) + goto nolights; + + CVector lightPos = mi->m_positions[CAR_POS_TAILLIGHTS]; + CVector lightR = GetMatrix() * lightPos; + CVector lightL = lightR; + lightL -= GetRight()*2.0f*lightPos.x; + + if(m_fBrakePedal > 0.0f || m_fGasPedal < 0.0f){ + CVector lookVector = GetPosition() - TheCamera.GetPosition(); + lookVector.Normalise(); + float behindness = DotProduct(lookVector, GetForward()); + if(behindness > 0.0f){ + if(m_fGasPedal < 0.0f){ + // reversing + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 2, 120, 120, 120, 255, + lightL, 1.2f, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 3, 120, 120, 120, 255, + lightR, 1.2f, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 4); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 4); + }else{ + // braking + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 2, 120, 0, 0, 255, + lightL, 1.2f, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 3, 120, 0, 0, 255, + lightR, 1.2f, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 8); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + 8); + } + }else{ + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 2, lightL, 50.0f*TheCamera.LODDistMultiplier, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 3, lightR, 50.0f*TheCamera.LODDistMultiplier, 0.0f); + } + }else{ + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 2, lightL, 50.0f*TheCamera.LODDistMultiplier, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 3, lightR, 50.0f*TheCamera.LODDistMultiplier, 0.0f); + } + } + +nolights: + CShadows::StoreShadowForCar(this); +} + +void +CAutomobile::Render(void) +{ + int i; + CMatrix mat; + CVector pos; + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + if(GetModelIndex() == MI_RHINO && m_aCarNodes[CAR_BONNET]){ + // Rhino has no bonnet...what are we doing here? + CMatrix m; + CVector p; + m.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BONNET])); + p = m.GetPosition(); + m.SetRotateZ(m_fCarGunLR); + m.Translate(p); + m.UpdateRW(); + } + + CVector contactPoints[4]; // relative to model + CVector contactSpeeds[4]; // speed at contact points + CVector frontWheelFwd = Multiply3x3(GetMatrix(), CVector(-Sin(m_fSteerAngle), Cos(m_fSteerAngle), 0.0f)); + CVector rearWheelFwd = GetForward(); + for(i = 0; i < 4; i++){ + contactPoints[i] = m_aWheelColPoints[i].point - GetPosition(); + contactSpeeds[i] = GetSpeed(contactPoints[i]); + if(i == CARWHEEL_FRONT_LEFT || i == CARWHEEL_FRONT_RIGHT) + m_aWheelSpeed[i] = ProcessWheelRotation(m_aWheelState[i], frontWheelFwd, contactSpeeds[i], 0.5f*mi->m_wheelScale); + else + m_aWheelSpeed[i] = ProcessWheelRotation(m_aWheelState[i], rearWheelFwd, contactSpeeds[i], 0.5f*mi->m_wheelScale); + m_aWheelRotation[i] += m_aWheelSpeed[i]; + } + + // Rear right wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RB])); + pos.x = mat.GetPosition().x; + pos.y = mat.GetPosition().y; + pos.z = m_aWheelPosition[CARWHEEL_REAR_RIGHT]; + if(Damage.GetWheelStatus(CARWHEEL_REAR_RIGHT) == WHEEL_STATUS_BURST) + mat.SetRotate(m_aWheelRotation[CARWHEEL_REAR_RIGHT], 0.0f, 0.3f*Sin(m_aWheelRotation[CARWHEEL_REAR_RIGHT])); + else + mat.SetRotateX(m_aWheelRotation[CARWHEEL_REAR_RIGHT]); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + if(CVehicle::bWheelsOnlyCheat) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RB])); + + // Rear left wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LB])); + pos.x = mat.GetPosition().x; + pos.y = mat.GetPosition().y; + pos.z = m_aWheelPosition[CARWHEEL_REAR_LEFT]; + if(Damage.GetWheelStatus(CARWHEEL_REAR_LEFT) == WHEEL_STATUS_BURST) + mat.SetRotate(-m_aWheelRotation[CARWHEEL_REAR_LEFT], 0.0f, PI+0.3f*Sin(-m_aWheelRotation[CARWHEEL_REAR_LEFT])); + else + mat.SetRotate(-m_aWheelRotation[CARWHEEL_REAR_LEFT], 0.0f, PI); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + if(CVehicle::bWheelsOnlyCheat) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB])); + + // Mid right wheel + if(m_aCarNodes[CAR_WHEEL_RM]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RM])); + pos.x = mat.GetPosition().x; + pos.y = mat.GetPosition().y; + pos.z = m_aWheelPosition[CARWHEEL_REAR_RIGHT]; + if(Damage.GetWheelStatus(CARWHEEL_REAR_RIGHT) == WHEEL_STATUS_BURST) + mat.SetRotate(m_aWheelRotation[CARWHEEL_REAR_RIGHT], 0.0f, 0.3f*Sin(m_aWheelRotation[CARWHEEL_REAR_RIGHT])); + else + mat.SetRotateX(m_aWheelRotation[CARWHEEL_REAR_RIGHT]); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + if(CVehicle::bWheelsOnlyCheat) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RM])); + } + + // Mid left wheel + if(m_aCarNodes[CAR_WHEEL_LM]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LM])); + pos.x = mat.GetPosition().x; + pos.y = mat.GetPosition().y; + pos.z = m_aWheelPosition[CARWHEEL_REAR_LEFT]; + if(Damage.GetWheelStatus(CARWHEEL_REAR_LEFT) == WHEEL_STATUS_BURST) + mat.SetRotate(-m_aWheelRotation[CARWHEEL_REAR_LEFT], 0.0f, PI+0.3f*Sin(-m_aWheelRotation[CARWHEEL_REAR_LEFT])); + else + mat.SetRotate(-m_aWheelRotation[CARWHEEL_REAR_LEFT], 0.0f, PI); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + if(CVehicle::bWheelsOnlyCheat) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LM])); + } + + if(GetModelIndex() == MI_DODO){ + // Front wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); + pos.x = mat.GetPosition().x; + pos.y = mat.GetPosition().y; + pos.z = m_aWheelPosition[CARWHEEL_FRONT_RIGHT]; + if(Damage.GetWheelStatus(CARWHEEL_FRONT_RIGHT) == WHEEL_STATUS_BURST) + mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, m_fSteerAngle+0.3f*Sin(m_aWheelRotation[CARWHEEL_FRONT_RIGHT])); + else + mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, m_fSteerAngle); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + if(CVehicle::bWheelsOnlyCheat) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF])); + + // Rotate propeller + if(m_aCarNodes[CAR_WINDSCREEN]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WINDSCREEN])); + pos = mat.GetPosition(); + mat.SetRotateY(m_fPropellerRotation); + mat.Translate(pos); + mat.UpdateRW(); + + m_fPropellerRotation += m_fGasPedal != 0.0f ? TWOPI/13.0f : TWOPI/26.0f; + if(m_fPropellerRotation > TWOPI) + m_fPropellerRotation -= TWOPI; + } + + // Rudder + if(Damage.GetDoorStatus(DOOR_BOOT) != DOOR_STATUS_MISSING && m_aCarNodes[CAR_BOOT]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BOOT])); + pos = mat.GetPosition(); + mat.SetRotate(0.0f, 0.0f, -m_fSteerAngle); + mat.Rotate(0.0f, Sin(m_fSteerAngle)*DEGTORAD(22.0f), 0.0f); + mat.Translate(pos); + mat.UpdateRW(); + } + + ProcessSwingingDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT); + ProcessSwingingDoor(CAR_DOOR_RF, DOOR_FRONT_RIGHT); + }else if(GetModelIndex() == MI_RHINO){ + // Front right wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); + pos.x = mat.GetPosition().x; + pos.y = mat.GetPosition().y; + pos.z = m_aWheelPosition[CARWHEEL_FRONT_RIGHT]; + // no damaged wheels or steering + mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, 0.0f); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + if(CVehicle::bWheelsOnlyCheat) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF])); + + // Front left wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF])); + pos.x = mat.GetPosition().x; + pos.y = mat.GetPosition().y; + pos.z = m_aWheelPosition[CARWHEEL_FRONT_LEFT]; + // no damaged wheels or steering + mat.SetRotate(-m_aWheelRotation[CARWHEEL_FRONT_LEFT], 0.0f, PI); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + if(CVehicle::bWheelsOnlyCheat) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF])); + }else{ + // Front right wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); + pos.x = mat.GetPosition().x; + pos.y = mat.GetPosition().y; + pos.z = m_aWheelPosition[CARWHEEL_FRONT_RIGHT]; + if(Damage.GetWheelStatus(CARWHEEL_FRONT_RIGHT) == WHEEL_STATUS_BURST) + mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, m_fSteerAngle+0.3f*Sin(m_aWheelRotation[CARWHEEL_FRONT_RIGHT])); + else + mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, m_fSteerAngle); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + if(CVehicle::bWheelsOnlyCheat) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF])); + + // Front left wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF])); + pos.x = mat.GetPosition().x; + pos.y = mat.GetPosition().y; + pos.z = m_aWheelPosition[CARWHEEL_FRONT_LEFT]; + if(Damage.GetWheelStatus(CARWHEEL_FRONT_LEFT) == WHEEL_STATUS_BURST) + mat.SetRotate(-m_aWheelRotation[CARWHEEL_FRONT_LEFT], 0.0f, PI+m_fSteerAngle+0.3f*Sin(-m_aWheelRotation[CARWHEEL_FRONT_LEFT])); + else + mat.SetRotate(-m_aWheelRotation[CARWHEEL_FRONT_LEFT], 0.0f, PI+m_fSteerAngle); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + if(CVehicle::bWheelsOnlyCheat) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF])); + + ProcessSwingingDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT); + ProcessSwingingDoor(CAR_DOOR_RF, DOOR_FRONT_RIGHT); + ProcessSwingingDoor(CAR_DOOR_LR, DOOR_REAR_LEFT); + ProcessSwingingDoor(CAR_DOOR_RR, DOOR_REAR_RIGHT); + ProcessSwingingDoor(CAR_BONNET, DOOR_BONNET); + ProcessSwingingDoor(CAR_BOOT, DOOR_BOOT); + + mi->SetVehicleColour(m_currentColour1, m_currentColour2); + } + + if(!CVehicle::bWheelsOnlyCheat) + CEntity::Render(); +} + +int32 +CAutomobile::ProcessEntityCollision(CEntity *ent, CColPoint *colpoints) +{ + int i; + CColModel *colModel; + + if(m_status != STATUS_SIMPLE) + bVehicleColProcessed = true; + + if(bUsingSpecialColModel) + colModel = &CWorld::Players[CWorld::PlayerInFocus].m_ColModel; + else + colModel = GetColModel(); + + int numWheelCollisions = 0; + float prevRatios[4] = { 0.0f, 0.0f, 0.0f, 0.0f}; + for(i = 0; i < 4; i++) + prevRatios[i] = m_aSuspensionSpringRatio[i]; + + int numCollisions = CCollision::ProcessColModels(GetMatrix(), *colModel, + ent->GetMatrix(), *ent->GetColModel(), + colpoints, + m_aWheelColPoints, m_aSuspensionSpringRatio); + + // m_aSuspensionSpringRatio are now set to the point where the tyre touches ground. + // In ProcessControl these will be re-normalized to ignore the tyre radius. + + if(field_EF || m_phy_flagA80 || + GetModelIndex() == MI_DODO && (ent->IsPed() || ent->IsVehicle())){ + // don't do line collision + for(i = 0; i < 4; i++) + m_aSuspensionSpringRatio[i] = prevRatios[i]; + }else{ + for(i = 0; i < 4; i++) + if(m_aSuspensionSpringRatio[i] < 1.0f && m_aSuspensionSpringRatio[i] < prevRatios[i]){ + numWheelCollisions++; + + // wheel is touching a physical + if(ent->IsVehicle() || ent->IsObject()){ + CPhysical *phys = (CPhysical*)ent; + + m_aGroundPhysical[i] = phys; + phys->RegisterReference((CEntity**)&m_aGroundPhysical[i]); + m_aGroundOffset[i] = m_aWheelColPoints[i].point - phys->GetPosition(); + + if(phys->GetModelIndex() == MI_BODYCAST && m_status == STATUS_PLAYER){ + // damage body cast + float speed = m_vecMoveSpeed.MagnitudeSqr(); + if(speed > 0.1f){ + CObject::nBodyCastHealth -= 0.1f*m_fMass*speed; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_PED_BODYCAST_HIT, 0.0f); + } + + // move body cast + if(phys->bIsStatic){ + phys->bIsStatic = false; + phys->m_nStaticFrames = 0; + phys->ApplyMoveForce(m_vecMoveSpeed / speed); + phys->AddToMovingList(); + } + } + } + + m_nSurfaceTouched = m_aWheelColPoints[i].surfaceB; + if(ent->IsBuilding()) + m_pCurGroundEntity = ent; + } + } + + if(numCollisions > 0 || numWheelCollisions > 0){ + AddCollisionRecord(ent); + if(!ent->IsBuilding()) + ((CPhysical*)ent)->AddCollisionRecord(this); + + if(numCollisions > 0) + if(ent->IsBuilding() || + ent->IsObject() && ((CPhysical*)ent)->bInfiniteMass) + bHasHitWall = true; + } + + return numCollisions; +} + +static int16 nLastControlInput; +static float fMouseCentreRange = 0.35f; +static float fMouseSteerSens = -0.0035f; +static float fMouseCentreMult = 0.975f; + +void +CAutomobile::ProcessControlInputs(uint8 pad) +{ + float speed = DotProduct(m_vecMoveSpeed, GetForward()); + + if(CPad::GetPad(pad)->GetExitVehicle()) + bIsHandbrakeOn = true; + else + bIsHandbrakeOn = !!CPad::GetPad(pad)->GetHandBrake(); + + // Steer left/right + if(CCamera::m_bUseMouse3rdPerson && !CVehicle::m_bDisableMouseSteering){ + if(CPad::GetPad(pad)->GetMouseX() != 0.0f){ + m_fSteerRatio += fMouseSteerSens*CPad::GetPad(pad)->GetMouseX(); + nLastControlInput = 2; + if(Abs(m_fSteerRatio) < fMouseCentreRange) + m_fSteerRatio *= Pow(fMouseCentreMult, CTimer::GetTimeStep()); + }else if(CPad::GetPad(pad)->GetSteeringLeftRight() || nLastControlInput != 2){ + // mouse hasn't move, steer with pad like below + m_fSteerRatio += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteerRatio)* + 0.2f*CTimer::GetTimeStep(); + nLastControlInput = 0; + } + }else{ + m_fSteerRatio += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteerRatio)* + 0.2f*CTimer::GetTimeStep(); + nLastControlInput = 0; + } + m_fSteerRatio = clamp(m_fSteerRatio, -1.0f, 1.0f); + + // Accelerate/Brake + float acceleration = (CPad::GetPad(pad)->GetAccelerate() - CPad::GetPad(pad)->GetBrake())/255.0f; + if(GetModelIndex() == MI_DODO && acceleration < 0.0f) + acceleration *= 0.3f; + if(Abs(speed) < 0.01f){ + // standing still, go into direction we want + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + }else{ +#if 1 + // simpler than the code below + if(speed * acceleration < 0.0f){ + // if opposite directions, have to brake first + m_fGasPedal = 0.0f; + m_fBrakePedal = Abs(acceleration); + }else{ + // accelerating in same direction we were already going + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + } +#else + if(speed < 0.0f){ + // moving backwards currently + if(acceleration < 0.0f){ + // still go backwards + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + }else{ + // want to go forwards, so brake + m_fGasPedal = 0.0f; + m_fBrakePedal = acceleration; + } + }else{ + // moving forwards currently + if(acceleration < 0.0f){ + // want to go backwards, so brake + m_fGasPedal = 0.0f; + m_fBrakePedal = -acceleration; + }else{ + // still go forwards + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + } + } +#endif + } + + // Actually turn wheels + static float fValue; // why static? + if(m_fSteerRatio < 0.0f) + fValue = -sq(m_fSteerRatio); + else + fValue = sq(m_fSteerRatio); + m_fSteerAngle = DEGTORAD(pHandling->fSteeringLock) * fValue; + + if(bComedyControls){ + int rnd = CGeneral::GetRandomNumber() % 10; + switch(m_comedyControlState){ + case 0: + if(rnd < 2) + m_comedyControlState = 1; + else if(rnd < 4) + m_comedyControlState = 2; + break; + case 1: + m_fSteerAngle += 0.05f; + if(rnd < 2) + m_comedyControlState = 0; + break; + case 2: + m_fSteerAngle -= 0.05f; + if(rnd < 2) + m_comedyControlState = 0; + break; + } + }else + m_comedyControlState = 0; + + // Brake if player isn't in control + // BUG: game always uses pad 0 here + if(CPad::GetPad(pad)->DisablePlayerControls){ + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + m_fGasPedal = 0.0f; + + FindPlayerPed()->KeepAreaAroundPlayerClear(); + + // slow down car immediately + speed = m_vecMoveSpeed.Magnitude(); + if(speed > 0.28f) + m_vecMoveSpeed *= 0.28f/speed; + } +} + +void +CAutomobile::FireTruckControl(void) +{ + if(this == FindPlayerVehicle()){ + if(!CPad::GetPad(0)->GetWeapon()) + return; + m_fCarGunLR += CPad::GetPad(0)->GetCarGunLeftRight()*0.00025f*CTimer::GetTimeStep(); + m_fCarGunUD += CPad::GetPad(0)->GetCarGunUpDown()*0.0001f*CTimer::GetTimeStep(); + m_fCarGunUD = clamp(m_fCarGunUD, 0.05f, 0.3f); + + CVector cannonPos(0.0f, 1.5f, 1.9f); + cannonPos = GetMatrix() * cannonPos; + CVector cannonDir( + Sin(m_fCarGunLR) * Cos(m_fCarGunUD), + Cos(m_fCarGunLR) * Cos(m_fCarGunUD), + Sin(m_fCarGunUD)); + cannonDir = Multiply3x3(GetMatrix(), cannonDir); + cannonDir.z += (CGeneral::GetRandomNumber()&0xF)/1000.0f; + CWaterCannons::UpdateOne((uintptr)this, &cannonPos, &cannonDir); + }else if(m_status == STATUS_PHYSICS){ + CFire *fire = gFireManager.FindFurthestFire_NeverMindFireMen(GetPosition(), 10.0f, 35.0f); + if(fire == nil) + return; + + // Target cannon onto fire + float targetAngle = CGeneral::GetATanOfXY(fire->m_vecPos.x-GetPosition().x, fire->m_vecPos.y-GetPosition().y); + float fwdAngle = CGeneral::GetATanOfXY(GetForward().x, GetForward().y); + float targetCannonAngle = fwdAngle - targetAngle; + float angleDelta = CTimer::GetTimeStep()*0.01f; + float cannonDelta = targetCannonAngle - m_fCarGunLR; + while(cannonDelta < PI) cannonDelta += TWOPI; + while(cannonDelta > PI) cannonDelta -= TWOPI; + if(Abs(cannonDelta) < angleDelta) + m_fCarGunLR = targetCannonAngle; + else if(cannonDelta > 0.0f) + m_fCarGunLR += angleDelta; + else + m_fCarGunLR -= angleDelta; + + // Go up and down a bit + float upDown = Sin((float)(CTimer::GetTimeInMilliseconds() & 0xFFF)/0x1000 * TWOPI); + m_fCarGunUD = 0.2f + 0.2f*upDown; + + // Spray water every once in a while + if((CTimer::GetTimeInMilliseconds()>>10) & 3){ + CVector cannonPos(0.0f, 0.0f, 2.2f); // different position than player's firetruck! + cannonPos = GetMatrix() * cannonPos; + CVector cannonDir( + Sin(m_fCarGunLR) * Cos(m_fCarGunUD), + Cos(m_fCarGunLR) * Cos(m_fCarGunUD), + Sin(m_fCarGunUD)); + cannonDir = Multiply3x3(GetMatrix(), cannonDir); + cannonDir.z += (CGeneral::GetRandomNumber()&0xF)/1000.0f; + CWaterCannons::UpdateOne((uintptr)this, &cannonPos, &cannonDir); + } + } +} + +void +CAutomobile::TankControl(void) +{ + int i; + + // These coords are 1 unit higher then they should be relative to model center + CVector turrentBase(0.0f, -1.394f, 2.296f); + CVector gunEnd(0.0f, 1.813f, 2.979f); + CVector baseToEnd = gunEnd - turrentBase; + + if(this != FindPlayerVehicle()) + return; + if(CWorld::Players[CWorld::PlayerInFocus].m_WBState != WBSTATE_PLAYING) + return; + + // Rotate turret + float prevAngle = m_fCarGunLR; + m_fCarGunLR -= CPad::GetPad(0)->GetCarGunLeftRight() * 0.00015f * CTimer::GetTimeStep(); + if(m_fCarGunLR < 0.0f) + m_fCarGunLR += TWOPI; + if(m_fCarGunLR > TWOPI) + m_fCarGunLR -= TWOPI; + if(m_fCarGunLR != prevAngle) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_TANK_TURRET_ROTATE, Abs(m_fCarGunLR - prevAngle)); + + // Shoot + if(CPad::GetPad(0)->CarGunJustDown() && + CTimer::GetTimeInMilliseconds() > CWorld::Players[CWorld::PlayerInFocus].m_nTimeTankShotGun + 800){ + CWorld::Players[CWorld::PlayerInFocus].m_nTimeTankShotGun = CTimer::GetTimeInMilliseconds(); + + // more like -sin(angle), cos(angle), i.e. rotated (0,1,0) + CVector turretDir = CVector(Sin(-m_fCarGunLR), Cos(-m_fCarGunLR), 0.0f); + turretDir = Multiply3x3(GetMatrix(), turretDir); + + float c = Cos(m_fCarGunLR); + float s = Sin(m_fCarGunLR); + CVector rotatedEnd( + c*baseToEnd.x - s*baseToEnd.y, + s*baseToEnd.x + c*baseToEnd.y, + baseToEnd.z - 1.0f); // correct offset here + rotatedEnd += turrentBase; + + CVector point1 = GetMatrix() * rotatedEnd; + CVector point2 = point1 + 60.0f*turretDir; + m_vecMoveSpeed -= 0.06f*turretDir; + m_vecMoveSpeed.z += 0.05f; + + CWeapon::DoTankDoomAiming(FindPlayerVehicle(), FindPlayerPed(), &point1, &point2); + CColPoint colpoint; + CEntity *entity = nil; + CWorld::ProcessLineOfSight(point1, point2, colpoint, entity, true, true, true, true, true, true, false); + if(entity) + point2 = colpoint.point - 0.04f*(colpoint.point - point1); + + CExplosion::AddExplosion(nil, FindPlayerPed(), EXPLOSION_TANK_GRENADE, point2, 0); + + // Add particles on the way to the explosion; + float shotDist = (point2 - point1).Magnitude(); + int n = shotDist/4.0f; + RwRGBA black = { 0, 0, 0, 0 }; + for(i = 0; i < n; i++){ + float f = (float)i/n; + CParticle::AddParticle(PARTICLE_HELI_DUST, + point1 + f*(point2 - point1), + CVector(0.0f, 0.0f, 0.0f), + nil, 0.1f, black); + } + + // More particles + CVector shotDir = point2 - point1; + shotDir.Normalise(); + for(i = 0; i < 15; i++){ + float f = i/15.0f; + CParticle::AddParticle(PARTICLE_GUNSMOKE2, point1, + shotDir*CGeneral::GetRandomNumberInRange(0.3f, 1.0f)*f, + nil, CGeneral::GetRandomNumberInRange(0.5f, 1.0f)*f, black); + } + + // And some gun flashes near the gun + CVector flashPos = point1; + CVector nullDir(0.0f, 0.0f, 0.0f); + int lifeSpan = 250; + if(m_vecMoveSpeed.Magnitude() > 0.08f){ + lifeSpan = 125; + flashPos.x += 0.5f*m_vecMoveSpeed.x; + flashPos.y += 0.5f*m_vecMoveSpeed.y; + } + CParticle::AddParticle(PARTICLE_GUNFLASH, flashPos, nullDir, nil, 0.4f, black, 0, 0, 0, lifeSpan); + flashPos += 0.3f*shotDir; + CParticle::AddParticle(PARTICLE_GUNFLASH, flashPos, nullDir, nil, 0.2f, black, 0, 0, 0, lifeSpan); + flashPos += 0.1f*shotDir; + CParticle::AddParticle(PARTICLE_GUNFLASH, flashPos, nullDir, nil, 0.15f, black, 0, 0, 0, lifeSpan); + } + + // Actually update turret node + if(m_aCarNodes[CAR_WINDSCREEN]){ + CMatrix mat; + CVector pos; + + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WINDSCREEN])); + pos = mat.GetPosition(); + mat.SetRotateZ(m_fCarGunLR); + mat.Translate(pos); + mat.UpdateRW(); + } +} + +void +CAutomobile::HydraulicControl(void) +{ + int i; + float wheelPositions[4]; + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + CColModel *normalColModel = mi->GetColModel(); + float wheelRadius = 0.5f*mi->m_wheelScale; + CPlayerInfo *playerInfo = &CWorld::Players[CWorld::PlayerInFocus]; + CColModel *specialColModel = &playerInfo->m_ColModel; + + if(m_status != STATUS_PLAYER){ + // reset hydraulics for non-player cars + + if(!bUsingSpecialColModel) + return; + if(specialColModel != nil) // this is always true + for(i = 0; i < 4; i++) + wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i]; + for(i = 0; i < 4; i++){ + m_aSuspensionSpringLength[i] = pHandling->fSuspensionUpperLimit - pHandling->fSuspensionLowerLimit; + m_aSuspensionLineLength[i] = normalColModel->lines[i].p0.z - normalColModel->lines[i].p1.z; + m_aSuspensionSpringRatio[i] = (normalColModel->lines[i].p0.z - wheelPositions[i]) / m_aSuspensionLineLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + + if(m_hydraulicState == 0) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_1, 0.0f); + else if(m_hydraulicState >= 100) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + + if(playerInfo->m_pVehicleEx == this) + playerInfo->m_pVehicleEx = nil; + bUsingSpecialColModel = false; + m_hydraulicState = 0; + return; + } + + // Player car + + float normalUpperLimit = pHandling->fSuspensionUpperLimit; + float normalLowerLimit = pHandling->fSuspensionLowerLimit; + float normalSpringLength = normalUpperLimit - normalLowerLimit; + float extendedUpperLimit = normalUpperLimit - 0.2f; + float extendedLowerLimit = normalLowerLimit - 0.2f; + float extendedSpringLength = extendedUpperLimit - extendedLowerLimit; + + if(!bUsingSpecialColModel){ + // Init special col model + + if(playerInfo->m_pVehicleEx && playerInfo->m_pVehicleEx == this) + playerInfo->m_pVehicleEx->bUsingSpecialColModel = false; + playerInfo->m_pVehicleEx = this; + playerInfo->m_ColModel = *normalColModel; + bUsingSpecialColModel = true; + specialColModel = &playerInfo->m_ColModel; + + if(m_fVelocityChangeForAudio > 0.1f) + m_hydraulicState = 20; + else{ + m_hydraulicState = 0; + normalUpperLimit += -0.12f; + normalSpringLength = normalUpperLimit - (normalLowerLimit+0.14f); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + } + + // Setup suspension + float normalLineLength = normalSpringLength + wheelRadius; + CVector pos; + for(i = 0; i < 4; i++){ + wheelPositions[i] = normalColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i]; + mi->GetWheelPosn(i, pos); + pos.z += normalUpperLimit; + specialColModel->lines[i].p0 = pos; + pos.z -= normalLineLength; + specialColModel->lines[i].p1 = pos; + m_aSuspensionSpringLength[i] = normalSpringLength; + m_aSuspensionLineLength[i] = normalLineLength; + + if(m_aSuspensionSpringRatio[i] < 1.0f){ + m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + } + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + + // Adjust col model + mi->GetWheelPosn(0, pos); + float minz = pos.z + extendedLowerLimit - wheelRadius; + if(minz < specialColModel->boundingBox.min.z) + specialColModel->boundingBox.min.z = minz; + float radius = Max(specialColModel->boundingBox.min.Magnitude(), specialColModel->boundingBox.max.Magnitude()); + if(specialColModel->boundingSphere.radius < radius) + specialColModel->boundingSphere.radius = radius; + + } + + if(playerInfo->m_WBState != WBSTATE_PLAYING) + return; + + bool setPrevRatio = false; + if(m_hydraulicState < 20 && m_fVelocityChangeForAudio > 0.2f){ + if(m_hydraulicState == 0){ + m_hydraulicState = 20; + for(i = 0; i < 4; i++) + m_aWheelPosition[i] -= 0.06f; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_1, 0.0f); + setPrevRatio = true; + }else{ + m_hydraulicState++; + } + }else if(m_hydraulicState != 0){ // must always be true + if(m_hydraulicState < 21 && m_fVelocityChangeForAudio < 0.1f){ + m_hydraulicState--; + if(m_hydraulicState == 0) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + } + } + + if(CPad::GetPad(0)->HornJustDown()){ + // Switch between normal and extended + + if(m_hydraulicState < 100) + m_hydraulicState = 100; + else{ + if(m_fVelocityChangeForAudio > 0.1f) + m_hydraulicState = 20; + else + m_hydraulicState = 0; + } + + if(m_hydraulicState < 100){ + if(m_hydraulicState == 0){ + normalUpperLimit += -0.12f; + normalLowerLimit += 0.14f; + normalSpringLength = normalUpperLimit - normalLowerLimit; + } + + // Reset suspension to normal + float normalLineLength = normalSpringLength + wheelRadius; + CVector pos; + for(i = 0; i < 4; i++){ + wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i]; + mi->GetWheelPosn(i, pos); + pos.z += normalUpperLimit; + specialColModel->lines[i].p0 = pos; + pos.z -= normalLineLength; + specialColModel->lines[i].p1 = pos; + m_aSuspensionSpringLength[i] = normalSpringLength; + m_aSuspensionLineLength[i] = normalLineLength; + + if(m_aSuspensionSpringRatio[i] < 1.0f){ + m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + } + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + }else{ + // Reset suspension to extended + float extendedLineLength = extendedSpringLength + wheelRadius; + CVector pos; + for(i = 0; i < 4; i++){ + wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i]; + mi->GetWheelPosn(i, pos); + pos.z += extendedUpperLimit; + specialColModel->lines[i].p0 = pos; + pos.z -= extendedLineLength; + specialColModel->lines[i].p1 = pos; + m_aSuspensionSpringLength[i] = extendedSpringLength; + m_aSuspensionLineLength[i] = extendedLineLength; + + if(m_aSuspensionSpringRatio[i] < 1.0f){ + m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + + setPrevRatio = true; + m_aWheelPosition[i] -= 0.05f; + } + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + } + }else{ + float suspChange[4]; + float maxDelta = 0.0f; + float rear = CPad::GetPad(0)->GetCarGunUpDown()/128.0f; + float front = -rear; + float right = CPad::GetPad(0)->GetCarGunLeftRight()/128.0f; + float left = -right; + suspChange[CARWHEEL_FRONT_LEFT] = Max(front+left, 0.0f); + suspChange[CARWHEEL_REAR_LEFT] = Max(rear+left, 0.0f); + suspChange[CARWHEEL_FRONT_RIGHT] = Max(front+right, 0.0f); + suspChange[CARWHEEL_REAR_RIGHT] = Max(rear+right, 0.0f); + + if(m_hydraulicState < 100){ + // Lowered, move wheels up + + if(m_hydraulicState == 0){ + normalUpperLimit += -0.12f; + normalLowerLimit += 0.14f; + normalSpringLength = normalUpperLimit - normalLowerLimit; + } + + // Set suspension + CVector pos; + for(i = 0; i < 4; i++){ + if(suspChange[i] > 1.0f) + suspChange[i] = 1.0f; + + float oldZ = specialColModel->lines[i].p1.z; + float upperLimit = suspChange[i]*(extendedUpperLimit-normalUpperLimit) + normalUpperLimit; + float springLength = suspChange[i]*(extendedSpringLength-normalSpringLength) + normalSpringLength; + float lineLength = springLength + wheelRadius; + + wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i]; + mi->GetWheelPosn(i, pos); + pos.z += upperLimit; + specialColModel->lines[i].p0 = pos; + pos.z -= lineLength; + if(Abs(pos.z - specialColModel->lines[i].p1.z) > Abs(maxDelta)) + maxDelta = pos.z - specialColModel->lines[i].p1.z; + specialColModel->lines[i].p1 = pos; + m_aSuspensionSpringLength[i] = springLength; + m_aSuspensionLineLength[i] = lineLength; + + if(m_aSuspensionSpringRatio[i] < 1.0f){ + m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + m_aWheelPosition[i] -= (oldZ - specialColModel->lines[i].p1.z)*0.3f; + } + } + }else{ + if(m_hydraulicState < 104){ + m_hydraulicState++; + for(i = 0; i < 4; i++) + m_aWheelPosition[i] -= 0.1f; + } + + if(m_fVelocityChangeForAudio < 0.1f){ + normalUpperLimit += -0.12f; + normalLowerLimit += 0.14f; + normalSpringLength = normalUpperLimit - normalLowerLimit; + } + + // Set suspension + CVector pos; + for(i = 0; i < 4; i++){ + if(suspChange[i] > 1.0f) + suspChange[i] = 1.0f; + + float upperLimit = suspChange[i]*(normalUpperLimit-extendedUpperLimit) + extendedUpperLimit; + float springLength = suspChange[i]*(normalSpringLength-extendedSpringLength) + extendedSpringLength; + float lineLength = springLength + wheelRadius; + + wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i]; + mi->GetWheelPosn(i, pos); + pos.z += upperLimit; + specialColModel->lines[i].p0 = pos; + pos.z -= lineLength; + if(Abs(pos.z - specialColModel->lines[i].p1.z) > Abs(maxDelta)) + maxDelta = pos.z - specialColModel->lines[i].p1.z; + specialColModel->lines[i].p1 = pos; + m_aSuspensionSpringLength[i] = springLength; + m_aSuspensionLineLength[i] = lineLength; + + if(m_aSuspensionSpringRatio[i] < 1.0f){ + m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + } + } + + float limitDiff = extendedLowerLimit - normalLowerLimit; + if(limitDiff != 0.0f && Abs(maxDelta/limitDiff) > 0.01f){ + float f = (maxDelta + limitDiff)/2.0f/limitDiff; + f = clamp(f, 0.0f, 1.0f); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_3, f); + if(f < 0.4f || f > 0.6f) + setPrevRatio = true; + if(f < 0.25f) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + else if(f > 0.75f) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_1, 0.0f); + } + } + + if(setPrevRatio) + for(i = 0; i < 4; i++){ + // wheel radius in relation to suspension line + float wheelRadius = 1.0f - m_aSuspensionSpringLength[i]/m_aSuspensionLineLength[i]; + m_aSuspensionSpringRatioPrev[i] = (m_aSuspensionSpringRatio[i]-wheelRadius)/(1.0f-wheelRadius); + } +} + +void +CAutomobile::ProcessBuoyancy(void) +{ + int i; + CVector impulse, point; + + if(mod_Buoyancy.ProcessBuoyancy(this, m_fBuoyancy, &point, &impulse)){ + m_flagD8 = true; + ApplyMoveForce(impulse); + ApplyTurnForce(impulse, point); + + CVector initialSpeed = m_vecMoveSpeed; + float timeStep = Max(CTimer::GetTimeStep(), 0.01f); + float impulseRatio = impulse.z / (GRAVITY * m_fMass * timeStep); + float waterResistance = Pow(1.0f - 0.05f*impulseRatio, CTimer::GetTimeStep()); + m_vecMoveSpeed *= waterResistance; + m_vecTurnSpeed *= waterResistance; + + if(impulseRatio > 0.5f){ + bIsInWater = true; + if(m_vecMoveSpeed.z < -0.1f) + m_vecMoveSpeed.z = -0.1f; + + if(pDriver){ + pDriver->bIsInWater = true; + if(pDriver->IsPlayer() || !bWaterTight) + pDriver->InflictDamage(nil, WEAPONTYPE_WATER, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); + } + for(i = 0; i < m_nNumMaxPassengers; i++) + if(pPassengers[i]){ + pPassengers[i]->bIsInWater = true; + if(pPassengers[i]->IsPlayer() || !bWaterTight) + pPassengers[i]->InflictDamage(nil, WEAPONTYPE_WATER, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); + } + }else + bIsInWater = false; + + static uint32 nGenerateRaindrops = 0; + static uint32 nGenerateWaterCircles = 0; + + if(initialSpeed.z < -0.3f && impulse.z > 0.3f){ + RwRGBA color; + color.red = (0.5f * CTimeCycle::GetDirectionalRed() + CTimeCycle::GetAmbientRed())*0.45f*255; + color.green = (0.5f * CTimeCycle::GetDirectionalGreen() + CTimeCycle::GetAmbientGreen())*0.45f*255; + color.blue = (0.5f * CTimeCycle::GetDirectionalBlue() + CTimeCycle::GetAmbientBlue())*0.45f*255; + color.alpha = CGeneral::GetRandomNumberInRange(0, 32) + 128; + CParticleObject::AddObject(POBJECT_CAR_WATER_SPLASH, GetPosition(), + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.15f, 0.3f)), + 0.0f, 75, color, true); + + nGenerateRaindrops = CTimer::GetTimeInMilliseconds() + 300; + nGenerateWaterCircles = CTimer::GetTimeInMilliseconds() + 60; + if(m_vecMoveSpeed.z < -0.2f) + m_vecMoveSpeed.z = -0.2f; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WATER_FALL, 0.0f); + } + + if(nGenerateWaterCircles > 0 && nGenerateWaterCircles < CTimer::GetTimeInMilliseconds()){ + CVector pos = GetPosition(); + float waterLevel = 0.0f; + if(CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &waterLevel, false)) + pos.z = waterLevel; + static RwRGBA black; + if(pos.z != 0.0f){ + nGenerateWaterCircles = 0; + pos.z += 1.0f; + for(i = 0; i < 4; i++){ + CVector p = pos; + p.x += CGeneral::GetRandomNumberInRange(-2.5f, 2.5f); + p.y += CGeneral::GetRandomNumberInRange(-2.5f, 2.5f); + CParticle::AddParticle(PARTICLE_RAIN_SPLASH_BIGGROW, + p, CVector(0.0f, 0.0f, 0.0f), + nil, 0.0f, black, 0, 0, 0, 0); + } + } + } + + if(nGenerateRaindrops > 0 && nGenerateRaindrops < CTimer::GetTimeInMilliseconds()){ + CVector pos = GetPosition(); + float waterLevel = 0.0f; + if(CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &waterLevel, false)) + pos.z = waterLevel; + static RwRGBA black; + if(pos.z >= 0.0f){ + nGenerateRaindrops = 0; + pos.z += 0.5; + CParticleObject::AddObject(POBJECT_SPLASHES_AROUND, + pos, CVector(0.0f, 0.0f, 0.0f), 6.5f, 2500, black, true); + } + } + }else{ + bIsInWater = false; + m_flagD8 = false; + + static RwRGBA splashCol = {155, 155, 185, 196}; + static RwRGBA smokeCol = {255, 255, 255, 255}; + + for(i = 0; i < 4; i++){ + if(m_aSuspensionSpringRatio[i] < 1.0f && m_aWheelColPoints[i].surfaceB == SURFACE_PUDDLE){ + CVector pos = m_aWheelColPoints[i].point + 0.3f*GetUp() - GetPosition(); + CVector vSpeed = GetSpeed(pos); + vSpeed.z = 0.0f; + float fSpeed = vSpeed.MagnitudeSqr(); + if(fSpeed > sq(0.05f)){ + fSpeed = Sqrt(fSpeed); + float size = Min((fSpeed < 0.15f ? 0.25f : 0.75f)*fSpeed, 0.6f); + CVector right = 0.2f*fSpeed*GetRight() + 0.2f*vSpeed; + + CParticle::AddParticle(PARTICLE_PED_SPLASH, + pos + GetPosition(), -0.5f*right, + nil, size, splashCol, + CGeneral::GetRandomNumberInRange(0.0f, 10.0f), + CGeneral::GetRandomNumberInRange(0.0f, 90.0f), 1, 0); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + pos + GetPosition(), -0.6f*right, + nil, size, smokeCol, 0, 0, 0, 0); + + if((CTimer::GetFrameCounter() & 0xF) == 0) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_SPLASH, 2000.0f*fSpeed); + } + } + } + } +} + +void +CAutomobile::DoDriveByShootings(void) +{ + CAnimBlendAssociation *anim; + CWeapon *weapon = pDriver->GetWeapon(); + if(weapon->m_eWeaponType != WEAPONTYPE_UZI) + return; + + weapon->Update(pDriver->m_audioEntityId); + + bool lookingLeft = false; + bool lookingRight = false; + if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN1){ + if(CPad::GetPad(0)->GetLookLeft()) + lookingLeft = true; + if(CPad::GetPad(0)->GetLookRight()) + lookingRight = true; + }else{ + if(TheCamera.Cams[TheCamera.ActiveCam].LookingLeft) + lookingLeft = true; + if(TheCamera.Cams[TheCamera.ActiveCam].LookingRight) + lookingRight = true; + } + + if(lookingLeft || lookingRight){ + if(lookingLeft){ + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_R); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_L); + if(anim == nil || anim->blendDelta < 0.0f) + CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, ANIM_DRIVEBY_L); + else + anim->SetRun(); + }else if(pDriver->m_pMyVehicle->pPassengers[0] == nil || TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_FIRSTPERSON){ + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_L); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_R); + if(anim == nil || anim->blendDelta < 0.0f) + CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, ANIM_DRIVEBY_R); + else + anim->SetRun(); + } + + if(CPad::GetPad(0)->GetCarGunFired() && CTimer::GetTimeInMilliseconds() > weapon->m_nTimer){ + weapon->FireFromCar(this, lookingLeft); + weapon->m_nTimer = CTimer::GetTimeInMilliseconds() + 70; + } + }else{ + weapon->Reload(); + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_L); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_DRIVEBY_R); + if(anim) + anim->blendDelta = -1000.0f; + } + + // TODO: what is this? + if(!lookingLeft && m_weaponDoorTimerLeft > 0.0f){ + m_weaponDoorTimerLeft = Max(m_weaponDoorTimerLeft - CTimer::GetTimeStep()*0.1f, 0.0f); + ProcessOpenDoor(CAR_DOOR_LF, NUM_ANIMS, m_weaponDoorTimerLeft); + } + if(!lookingRight && m_weaponDoorTimerRight > 0.0f){ + m_weaponDoorTimerRight = Max(m_weaponDoorTimerRight - CTimer::GetTimeStep()*0.1f, 0.0f); + ProcessOpenDoor(CAR_DOOR_RF, NUM_ANIMS, m_weaponDoorTimerRight); + } +} + +int32 +CAutomobile::RcbanditCheckHitWheels(void) +{ + int x, xmin, xmax; + int y, ymin, ymax; + + xmin = CWorld::GetSectorIndexX(GetPosition().x - 2.0f); + if(xmin < 0) xmin = 0; + xmax = CWorld::GetSectorIndexX(GetPosition().x + 2.0f); + if(xmax > NUMSECTORS_X-1) xmax = NUMSECTORS_X-1; + ymin = CWorld::GetSectorIndexX(GetPosition().y - 2.0f); + if(ymin < 0) ymin = 0; + ymax = CWorld::GetSectorIndexX(GetPosition().y + 2.0f); + if(ymax > NUMSECTORS_Y-1) ymax = NUMSECTORS_X-1; + + CWorld::AdvanceCurrentScanCode(); + + for(y = ymin; y <= ymax; y++) + for(x = xmin; x <= xmax; x++){ + CSector *s = CWorld::GetSector(x, y); + if(RcbanditCheck1CarWheels(s->m_lists[ENTITYLIST_VEHICLES]) || + RcbanditCheck1CarWheels(s->m_lists[ENTITYLIST_VEHICLES_OVERLAP])) + return 1; + } + return 0; +} + +int32 +CAutomobile::RcbanditCheck1CarWheels(CPtrList &list) +{ + static CMatrix matW2B; + int i; + CPtrNode *node; + CAutomobile *car; + CColModel *colModel = GetColModel(); + CVehicleModelInfo *mi; + + for(node = list.first; node; node = node->next){ + car = (CAutomobile*)node->item; + if(this != car && car->IsCar() && car->m_scanCode != CWorld::GetCurrentScanCode()){ + car->m_scanCode = CWorld::GetCurrentScanCode(); + + if(Abs(this->GetPosition().x - car->GetPosition().x) < 10.0f && + Abs(this->GetPosition().y - car->GetPosition().y) < 10.0f){ + mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(car->GetModelIndex()); + + for(i = 0; i < 4; i++){ + if(car->m_aSuspensionSpringRatioPrev[i] < 1.0f || car->m_status == STATUS_SIMPLE){ + CVector wheelPos; + CColSphere sph; + mi->GetWheelPosn(i, wheelPos); + matW2B = Invert(GetMatrix()); + sph.center = matW2B * (car->GetMatrix() * wheelPos); + sph.radius = mi->m_wheelScale*0.25f; + if(CCollision::TestSphereBox(sph, colModel->boundingBox)) + return 1; + } + } + } + } + } + return 0; +} + +void +CAutomobile::PlaceOnRoadProperly(void) +{ + CColPoint point; + CEntity *entity; + CColModel *colModel = GetColModel(); + float lenFwd, lenBack; + float frontZ, rearZ; + + lenFwd = colModel->boundingBox.max.y; + lenBack = -colModel->boundingBox.min.y; + + CVector front(GetPosition().x + GetForward().x*lenFwd, + GetPosition().y + GetForward().y*lenFwd, + GetPosition().z + 5.0f); + if(CWorld::ProcessVerticalLine(front, GetPosition().z - 5.0f, point, entity, + true, false, false, false, false, false, nil)){ + frontZ = point.point.z; + m_pCurGroundEntity = entity; + }else{ + frontZ = field_21C; + } + + CVector rear(GetPosition().x - GetForward().x*lenBack, + GetPosition().y - GetForward().y*lenBack, + GetPosition().z + 5.0f); + if(CWorld::ProcessVerticalLine(rear, GetPosition().z - 5.0f, point, entity, + true, false, false, false, false, false, nil)){ + rearZ = point.point.z; + m_pCurGroundEntity = entity; + }else{ + rearZ = field_220; + } + + float len = lenFwd + lenBack; + float angle = Atan((frontZ - rearZ)/len); + float c = Cos(angle); + float s = Sin(angle); + + GetRight() = CVector((front.y - rear.y)/len, -(front.x - rear.x)/len, 0.0f); + GetForward() = CVector(-c*GetRight().y, c*GetRight().x, s); + GetUp() = CrossProduct(GetRight(), GetForward()); + GetPosition() = CVector((front.x + rear.x)/2.0f, (front.y + rear.y)/2.0f, (frontZ + rearZ)/2.0f + GetHeightAboveRoad()); +} + +void +CAutomobile::VehicleDamage(float impulse, uint16 damagedPiece) +{ + int i; + float damageMultiplier = 0.2f; + bool doubleMoney = false; + + if(impulse == 0.0f){ + impulse = m_fDamageImpulse; + damagedPiece = m_nDamagePieceType; + damageMultiplier = 1.0f; + } + + CVector pos(0.0f, 0.0f, 0.0f); + + if(!bCanBeDamaged) + return; + + // damage flipped over car + if(GetUp().z < 0.0f && this != FindPlayerVehicle()){ + if(bNotDamagedUpsideDown || m_status == STATUS_PLAYER_REMOTE || bIsInWater) + return; + m_fHealth -= 4.0f*CTimer::GetTimeStep(); + } + + if(impulse > 25.0f && m_status != STATUS_WRECKED){ + if(bIsLawEnforcer && + FindPlayerVehicle() && FindPlayerVehicle() == m_pDamageEntity && + m_status != STATUS_ABANDONED && + FindPlayerVehicle()->m_vecMoveSpeed.Magnitude() >= m_vecMoveSpeed.Magnitude() && + FindPlayerVehicle()->m_vecMoveSpeed.Magnitude() > 0.1f) + FindPlayerPed()->SetWantedLevelNoDrop(1); + + if(m_status == STATUS_PLAYER && impulse > 50.0f){ + uint8 freq = Min(0.4f*impulse*2000.0f/m_fMass + 100.0f, 250.0f); + CPad::GetPad(0)->StartShake(40000/freq, freq); + } + + if(bOnlyDamagedByPlayer){ + if(m_pDamageEntity != FindPlayerPed() && + m_pDamageEntity != FindPlayerVehicle()) + return; + } + + if(bCollisionProof) + return; + + if(m_pDamageEntity){ + if(m_pDamageEntity->IsBuilding() && + DotProduct(m_vecDamageNormal, GetUp()) > 0.6f) + return; + } + + int oldLightStatus[4]; + for(i = 0; i < 4; i++) + oldLightStatus[i] = Damage.GetLightStatus((eLights)i); + + if(GetUp().z > 0.0f || m_vecMoveSpeed.MagnitudeSqr() > 0.1f){ + float impulseMult = bMoreResistantToDamage ? 0.5f : 4.0f; + + switch(damagedPiece){ + case CAR_PIECE_BUMP_FRONT: + GetComponentWorldPosition(CAR_BUMP_FRONT, pos); + dmgDrawCarCollidingParticles(pos, impulse); + if(Damage.ApplyDamage(COMPONENT_BUMPER_FRONT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + SetBumperDamage(CAR_BUMP_FRONT, VEHBUMPER_FRONT); + doubleMoney = true; + } + if(m_aCarNodes[CAR_BONNET] && Damage.GetPanelStatus(VEHBUMPER_FRONT) == PANEL_STATUS_MISSING){ + case CAR_PIECE_BONNET: + GetComponentWorldPosition(CAR_BONNET, pos); + dmgDrawCarCollidingParticles(pos, impulse); + if(Damage.ApplyDamage(COMPONENT_DOOR_BONNET, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + SetDoorDamage(CAR_BONNET, DOOR_BONNET); + doubleMoney = true; + } + } + break; + + case CAR_PIECE_BUMP_REAR: + GetComponentWorldPosition(CAR_BUMP_REAR, pos); + dmgDrawCarCollidingParticles(pos, impulse); + if(Damage.ApplyDamage(COMPONENT_BUMPER_FRONT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + SetBumperDamage(CAR_BUMP_REAR, VEHBUMPER_REAR); + doubleMoney = true; + } + if(m_aCarNodes[CAR_BOOT] && Damage.GetPanelStatus(VEHBUMPER_REAR) == PANEL_STATUS_MISSING){ + case CAR_PIECE_BOOT: + GetComponentWorldPosition(CAR_BOOT, pos); + dmgDrawCarCollidingParticles(pos, impulse); + if(Damage.ApplyDamage(COMPONENT_DOOR_BOOT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + SetDoorDamage(CAR_BOOT, DOOR_BOOT); + doubleMoney = true; + } + } + break; + + case CAR_PIECE_DOOR_LF: + GetComponentWorldPosition(CAR_DOOR_LF, pos); + dmgDrawCarCollidingParticles(pos, impulse); + if((m_nDoorLock == CARLOCK_NOT_USED || m_nDoorLock == CARLOCK_UNLOCKED) && + Damage.ApplyDamage(COMPONENT_DOOR_FRONT_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + SetDoorDamage(CAR_DOOR_LF, DOOR_FRONT_LEFT); + doubleMoney = true; + } + break; + case CAR_PIECE_DOOR_RF: + GetComponentWorldPosition(CAR_DOOR_RF, pos); + dmgDrawCarCollidingParticles(pos, impulse); + if((m_nDoorLock == CARLOCK_NOT_USED || m_nDoorLock == CARLOCK_UNLOCKED) && + Damage.ApplyDamage(COMPONENT_DOOR_FRONT_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + SetDoorDamage(CAR_DOOR_RF, DOOR_FRONT_RIGHT); + doubleMoney = true; + } + break; + case CAR_PIECE_DOOR_LR: + GetComponentWorldPosition(CAR_DOOR_LR, pos); + dmgDrawCarCollidingParticles(pos, impulse); + if((m_nDoorLock == CARLOCK_NOT_USED || m_nDoorLock == CARLOCK_UNLOCKED) && + Damage.ApplyDamage(COMPONENT_DOOR_REAR_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + SetDoorDamage(CAR_DOOR_LR, DOOR_REAR_LEFT); + doubleMoney = true; + } + break; + case CAR_PIECE_DOOR_RR: + GetComponentWorldPosition(CAR_DOOR_RR, pos); + dmgDrawCarCollidingParticles(pos, impulse); + if((m_nDoorLock == CARLOCK_NOT_USED || m_nDoorLock == CARLOCK_UNLOCKED) && + Damage.ApplyDamage(COMPONENT_DOOR_REAR_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + SetDoorDamage(CAR_DOOR_RR, DOOR_REAR_RIGHT); + doubleMoney = true; + } + break; + + case CAR_PIECE_WING_LF: + GetComponentWorldPosition(CAR_WING_LF, pos); + dmgDrawCarCollidingParticles(pos, impulse); + if(Damage.ApplyDamage(COMPONENT_PANEL_FRONT_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + SetPanelDamage(CAR_WING_LF, VEHPANEL_FRONT_LEFT); + doubleMoney = true; + } + break; + case CAR_PIECE_WING_RF: + GetComponentWorldPosition(CAR_WING_RF, pos); + dmgDrawCarCollidingParticles(pos, impulse); + if(Damage.ApplyDamage(COMPONENT_PANEL_FRONT_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + SetPanelDamage(CAR_WING_RF, VEHPANEL_FRONT_RIGHT); + doubleMoney = true; + } + break; + case CAR_PIECE_WING_LR: + GetComponentWorldPosition(CAR_WING_LR, pos); + dmgDrawCarCollidingParticles(pos, impulse); + if(Damage.ApplyDamage(COMPONENT_PANEL_REAR_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + SetPanelDamage(CAR_WING_LR, VEHPANEL_REAR_LEFT); + doubleMoney = true; + } + break; + case CAR_PIECE_WING_RR: + GetComponentWorldPosition(CAR_WING_RR, pos); + dmgDrawCarCollidingParticles(pos, impulse); + if(Damage.ApplyDamage(COMPONENT_PANEL_REAR_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + SetPanelDamage(CAR_WING_RR, VEHPANEL_REAR_RIGHT); + doubleMoney = true; + } + break; + + case CAR_PIECE_WHEEL_LF: + case CAR_PIECE_WHEEL_LR: + case CAR_PIECE_WHEEL_RF: + case CAR_PIECE_WHEEL_RR: + break; + + case CAR_PIECE_WINDSCREEN: + if(Damage.ApplyDamage(COMPONENT_PANEL_WINDSCREEN, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + uint8 oldStatus = Damage.GetPanelStatus(VEHPANEL_WINDSCREEN); + SetPanelDamage(CAR_WINDSCREEN, VEHPANEL_WINDSCREEN); + if(oldStatus != Damage.GetPanelStatus(VEHPANEL_WINDSCREEN)){ + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_WINDSHIELD_CRACK, 0.0f); + doubleMoney = true; + } + } + break; + } + + if(m_pDamageEntity && m_pDamageEntity == FindPlayerVehicle() && impulse > 10.0f){ + int money = (doubleMoney ? 2 : 1) * impulse*pHandling->nMonetaryValue/1000000.0f; + money = Min(money, 40); + if(money > 2){ + sprintf(gString, "$%d", money); + CWorld::Players[CWorld::PlayerInFocus].m_nMoney += money; + } + } + } + + float damage = (impulse-25.0f)*pHandling->fCollisionDamageMultiplier*0.6f*damageMultiplier; + + if(GetModelIndex() == MI_SECURICA && m_pDamageEntity && m_pDamageEntity->m_status == STATUS_PLAYER) + damage *= 7.0f; + + if(damage > 0.0f){ + int oldHealth = m_fHealth; + if(this == FindPlayerVehicle()){ + m_fHealth -= bTakeLessDamage ? damage/6.0f : damage/2.0f; + }else{ + if(damage > 35.0f && pDriver) + pDriver->Say(SOUND_PED_CAR_COLLISION); + m_fHealth -= bTakeLessDamage ? damage/12.0f : damage/4.0f; + } + if(m_fHealth <= 0.0f && oldHealth > 0) + m_fHealth = 1.0f; + } + + // play sound if a light broke + for(i = 0; i < 4; i++) + if(oldLightStatus[i] != 1 && Damage.GetLightStatus((eLights)i) == 1){ + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_LIGHT_BREAK, i); // BUG? i? + break; + } + } + + if(m_fHealth < 250.0f){ + // Car is on fire + if(Damage.GetEngineStatus() < ENGINE_STATUS_ON_FIRE){ + // Set engine on fire and remember who did this + Damage.SetEngineStatus(ENGINE_STATUS_ON_FIRE); + m_fFireBlowUpTimer = 0.0f; + m_pSetOnFireEntity = m_pDamageEntity; + if(m_pSetOnFireEntity) + m_pSetOnFireEntity->RegisterReference(&m_pSetOnFireEntity); + } + }else{ + if(GetModelIndex() == MI_BFINJECT){ + if(m_fHealth < 400.0f) + Damage.SetEngineStatus(200); + else if(m_fHealth < 600.0f) + Damage.SetEngineStatus(100); + } + } +} + +void +CAutomobile::dmgDrawCarCollidingParticles(const CVector &pos, float amount) +{ + int i, n; + + if(!GetIsOnScreen()) + return; + + // FindPlayerSpeed() unused + + n = (int)amount/20; + + for(i = 0; i < ((n+4)&0x1F); i++) + CParticle::AddParticle(PARTICLE_SPARK_SMALL, pos, + CVector(CGeneral::GetRandomNumberInRange(-0.1f, 0.1f), + CGeneral::GetRandomNumberInRange(-0.1f, 0.1f), + 0.006f)); + + for(i = 0; i < n+2; i++) + CParticle::AddParticle(PARTICLE_CARCOLLISION_DUST, + CVector(CGeneral::GetRandomNumberInRange(-1.2f, 1.2f) + pos.x, + CGeneral::GetRandomNumberInRange(-1.2f, 1.2f) + pos.y, + pos.z), + CVector(0.0f, 0.0f, 0.0f), nil, 0.5f); + + n = (int)amount/50 + 1; + for(i = 0; i < n; i++) + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, pos, + CVector(CGeneral::GetRandomNumberInRange(-0.25f, 0.25f), + CGeneral::GetRandomNumberInRange(-0.25f, 0.25f), + CGeneral::GetRandomNumberInRange(0.1f, 0.25f)), + nil, + CGeneral::GetRandomNumberInRange(0.02f, 0.08f), + CVehicleModelInfo::ms_vehicleColourTable[m_currentColour1], + CGeneral::GetRandomNumberInRange(-40.0f, 40.0f), + 0, + CGeneral::GetRandomNumberInRange(0.0f, 4.0f)); +} + +void +CAutomobile::AddDamagedVehicleParticles(void) +{ + if(this == FindPlayerVehicle() && TheCamera.GetLookingForwardFirstPerson()) + return; + + uint8 engineStatus = Damage.GetEngineStatus(); + if(engineStatus < ENGINE_STATUS_STEAM1) + return; + + float fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward()) * 180.0f; + CVector direction = 0.5f*m_vecMoveSpeed; + CVector damagePos = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->m_positions[CAR_POS_HEADLIGHTS]; + + switch(Damage.GetDoorStatus(DOOR_BONNET)){ + case DOOR_STATUS_OK: + case DOOR_STATUS_SMASHED: + // Bonnet is still there, smoke comes out at the edge + damagePos += vecDAMAGE_ENGINE_POS_SMALL; + break; + case DOOR_STATUS_SWINGING: + case DOOR_STATUS_MISSING: + // Bonnet is gone, smoke comes out at the engine + damagePos += vecDAMAGE_ENGINE_POS_BIG; + break; + } + + if(GetModelIndex() == MI_BFINJECT) + damagePos = CVector(0.3f, -1.5f, -0.1f); + + damagePos = GetMatrix()*damagePos; + damagePos.z += 0.15f; + + if(engineStatus < ENGINE_STATUS_STEAM2){ + if(fwdSpeed < 90.0f){ + direction.z += 0.05f; + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction, nil, 0.1f); + } + }else if(engineStatus < ENGINE_STATUS_SMOKE){ + if(fwdSpeed < 90.0f) + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction, nil, 0.0f); + }else if(engineStatus < ENGINE_STATUS_ON_FIRE){ + if(fwdSpeed < 90.0f){ + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction, nil, 0.0f); + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, damagePos, 0.3f*direction, nil, 0.0f); + } + }else if(m_fHealth > 250.0f){ + if(fwdSpeed < 90.0f) + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, damagePos, 0.2f*direction, nil, 0.0f); + } +} + +int32 +CAutomobile::AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed) +{ + int i; + CVector dir; + static RwRGBA grassCol = { 8, 24, 8, 255 }; + static RwRGBA dirtCol = { 64, 64, 64, 255 }; + static RwRGBA dirttrackCol = { 64, 32, 16, 255 }; + static RwRGBA waterCol = { 48, 48, 64, 0 }; + + if(!belowEffectSpeed) + return 0; + + switch(colpoint->surfaceB){ + case SURFACE_GRASS: + dir.x = -0.05f*m_vecMoveSpeed.x; + dir.y = -0.05f*m_vecMoveSpeed.y; + for(i = 0; i < 4; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.06f); + CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, + CGeneral::GetRandomNumberInRange(0.02f, 0.1f), grassCol); + } + return 0; + case SURFACE_DIRT: + dir.x = -0.05f*m_vecMoveSpeed.x; + dir.y = -0.05f*m_vecMoveSpeed.y; + for(i = 0; i < 4; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.06f); + CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, + CGeneral::GetRandomNumberInRange(0.02f, 0.06f), dirtCol); + } + return 1; + case SURFACE_DIRTTRACK: + dir.x = -0.05f*m_vecMoveSpeed.x; + dir.y = -0.05f*m_vecMoveSpeed.y; + for(i = 0; i < 4; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.06f); + CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, + CGeneral::GetRandomNumberInRange(0.02f, 0.06f), dirttrackCol); + } + return 0; + default: + // Is this even visible? + if(CWeather::WetRoads > 0.01f && CTimer::GetFrameCounter() & 1){ + CParticle::AddParticle(PARTICLE_WATERSPRAY, + colpoint->point + CVector(0.0f, 0.0f, 0.25f+0.25f), + CVector(0.0f, 0.0f, 1.0f), nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.5f), waterCol); + return 0; + } + return 1; + } +} + +void +CAutomobile::GetComponentWorldPosition(int32 component, CVector &pos) +{ + if(m_aCarNodes[component] == nil){ + printf("CarNode missing: %d %d\n", GetModelIndex(), component); + return; + } + RwMatrix *ltm = RwFrameGetLTM(m_aCarNodes[component]); + pos = *RwMatrixGetPos(ltm); +} + +bool +CAutomobile::IsComponentPresent(int32 comp) +{ + return m_aCarNodes[comp] != nil; +} + +void +CAutomobile::SetComponentRotation(int32 component, CVector rotation) +{ + CMatrix mat(RwFrameGetMatrix(m_aCarNodes[component])); + CVector pos = mat.GetPosition(); + // BUG: all these set the whole matrix + mat.SetRotateX(DEGTORAD(rotation.x)); + mat.SetRotateY(DEGTORAD(rotation.y)); + mat.SetRotateZ(DEGTORAD(rotation.z)); + mat.Translate(pos); + mat.UpdateRW(); +} + +void +CAutomobile::OpenDoor(int32 component, eDoors door, float openRatio) +{ + CMatrix mat(RwFrameGetMatrix(m_aCarNodes[component])); + CVector pos = mat.GetPosition(); + float axes[3] = { 0.0f, 0.0f, 0.0f }; + float wasClosed = false; + + if(Doors[door].IsClosed()){ + // enable angle cull for closed doors + RwFrameForAllObjects(m_aCarNodes[component], CVehicleModelInfo::ClearAtomicFlagCB, (void*)ATOMIC_FLAG_NOCULL); + wasClosed = true; + } + + Doors[door].Open(openRatio); + + if(wasClosed && Doors[door].RetAngleWhenClosed() != Doors[door].m_fAngle){ + // door opened + HideAllComps(); + // turn off angle cull for swinging door + RwFrameForAllObjects(m_aCarNodes[component], CVehicleModelInfo::SetAtomicFlagCB, (void*)ATOMIC_FLAG_NOCULL); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_DOOR_OPEN_BONNET + door, 0.0f); + } + + if(!wasClosed && openRatio == 0.0f){ + // door closed + if(Damage.GetDoorStatus(door) == DOOR_STATUS_SWINGING) + Damage.SetDoorStatus(door, DOOR_STATUS_OK); // huh? + ShowAllComps(); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_DOOR_CLOSE_BONNET + door, 0.0f); + } + + axes[Doors[door].m_nAxis] = Doors[door].m_fAngle; + mat.SetRotate(axes[0], axes[1], axes[2]); + mat.Translate(pos); + mat.UpdateRW(); +} + +inline void ProcessDoorOpenAnimation(CAutomobile *car, uint32 component, eDoors door, float time, float start, float end) +{ + if(time > start && time < end){ + float ratio = (time - start)/(end - start); + if(car->Doors[door].GetAngleOpenRatio() < ratio) + car->OpenDoor(component, door, ratio); + }else if(time > end){ + car->OpenDoor(component, door, 1.0f); + } +} + +inline void ProcessDoorCloseAnimation(CAutomobile *car, uint32 component, eDoors door, float time, float start, float end) +{ + if(time > start && time < end){ + float ratio = 1.0f - (time - start)/(end - start); + if(car->Doors[door].GetAngleOpenRatio() > ratio) + car->OpenDoor(component, door, ratio); + }else if(time > end){ + car->OpenDoor(component, door, 0.0f); + } +} + +inline void ProcessDoorOpenCloseAnimation(CAutomobile *car, uint32 component, eDoors door, float time, float start, float mid, float end) +{ + if(time > start && time < mid){ + // open + float ratio = (time - start)/(mid - start); + if(car->Doors[door].GetAngleOpenRatio() < ratio) + car->OpenDoor(component, door, ratio); + }else if(time > mid && time < end){ + // close + float ratio = 1.0f - (time - mid)/(end - mid); + if(car->Doors[door].GetAngleOpenRatio() > ratio) + car->OpenDoor(component, door, ratio); + }else if(time > end){ + car->OpenDoor(component, door, 0.0f); + } +} +void +CAutomobile::ProcessOpenDoor(uint32 component, uint32 anim, float time) +{ + eDoors door; + + switch(component){ + case CAR_DOOR_RF: door = DOOR_FRONT_RIGHT; break; + case CAR_DOOR_RR: door = DOOR_REAR_RIGHT; break; + case CAR_DOOR_LF: door = DOOR_FRONT_LEFT; break; + case CAR_DOOR_LR: door = DOOR_REAR_LEFT; break; + default: assert(0); + } + + if(IsDoorMissing(door)) + return; + + switch(anim){ + case ANIM_CAR_QJACK: + case ANIM_CAR_OPEN_LHS: + case ANIM_CAR_OPEN_RHS: + ProcessDoorOpenAnimation(this, component, door, time, 0.66f, 0.8f); + break; + case ANIM_CAR_CLOSEDOOR_LHS: + case ANIM_CAR_CLOSEDOOR_LOW_LHS: + case ANIM_CAR_CLOSEDOOR_RHS: + case ANIM_CAR_CLOSEDOOR_LOW_RHS: + ProcessDoorCloseAnimation(this, component, door, time, 0.2f, 0.63f); + break; + case ANIM_CAR_ROLLDOOR: + case ANIM_CAR_ROLLDOOR_LOW: + ProcessDoorOpenCloseAnimation(this, component, door, time, 0.1f, 0.6f, 0.95f); + break; + break; + case ANIM_CAR_GETOUT_LHS: + case ANIM_CAR_GETOUT_LOW_LHS: + case ANIM_CAR_GETOUT_RHS: + case ANIM_CAR_GETOUT_LOW_RHS: + ProcessDoorOpenAnimation(this, component, door, time, 0.06f, 0.43f); + break; + case ANIM_CAR_CLOSE_LHS: + case ANIM_CAR_CLOSE_RHS: + ProcessDoorCloseAnimation(this, component, door, time, 0.1f, 0.23f); + break; + case ANIM_CAR_PULLOUT_RHS: + case ANIM_CAR_PULLOUT_LOW_RHS: + OpenDoor(component, door, 1.0f); + case ANIM_COACH_OPEN_L: + case ANIM_COACH_OPEN_R: + ProcessDoorOpenAnimation(this, component, door, time, 0.66f, 0.8f); + break; + case ANIM_COACH_OUT_L: + ProcessDoorOpenAnimation(this, component, door, time, 0.0f, 0.3f); + break; + case ANIM_VAN_OPEN_L: + case ANIM_VAN_OPEN: + ProcessDoorOpenAnimation(this, component, door, time, 0.37f, 0.55f); + break; + case ANIM_VAN_CLOSE_L: + case ANIM_VAN_CLOSE: + ProcessDoorCloseAnimation(this, component, door, time, 0.5f, 0.8f); + break; + case ANIM_VAN_GETOUT_L: + case ANIM_VAN_GETOUT: + ProcessDoorOpenAnimation(this, component, door, time, 0.5f, 0.6f); + break; + case NUM_ANIMS: + OpenDoor(component, door, time); + break; + } +} + +bool +CAutomobile::IsDoorReady(eDoors door) +{ + if(Doors[door].IsClosed() || IsDoorMissing(door)) + return true; + int doorflag = 0; + // TODO: enum? + switch(door){ + case DOOR_FRONT_LEFT: doorflag = 1; break; + case DOOR_FRONT_RIGHT: doorflag = 4; break; + case DOOR_REAR_LEFT: doorflag = 2; break; + case DOOR_REAR_RIGHT: doorflag = 8; break; + } + return (doorflag & m_nGettingInFlags) == 0; +} + +bool +CAutomobile::IsDoorFullyOpen(eDoors door) +{ + return Doors[door].IsFullyOpen() || IsDoorMissing(door); +} + +bool +CAutomobile::IsDoorClosed(eDoors door) +{ + return !!Doors[door].IsClosed(); +} + +bool +CAutomobile::IsDoorMissing(eDoors door) +{ + return Damage.GetDoorStatus(door) == DOOR_STATUS_MISSING; +} + +void +CAutomobile::RemoveRefsToVehicle(CEntity *ent) +{ + int i; + for(i = 0; i < 4; i++) + if(m_aGroundPhysical[i] == ent) + m_aGroundPhysical[i] = nil; +} + +void +CAutomobile::BlowUpCar(CEntity *culprit) +{ + int i; + RpAtomic *atomic; + + if(!bCanBeDamaged) + return; + + // explosion pushes vehicle up + m_vecMoveSpeed.z += 0.13f; + m_status = STATUS_WRECKED; + bRenderScorched = true; + m_nTimeOfDeath = CTimer::GetTimeInMilliseconds(); + Damage.FuckCarCompletely(); + + if(GetModelIndex() != MI_RCBANDIT){ + SetBumperDamage(CAR_BUMP_FRONT, VEHBUMPER_FRONT); + SetBumperDamage(CAR_BUMP_REAR, VEHBUMPER_REAR); + SetDoorDamage(CAR_BONNET, DOOR_BONNET); + SetDoorDamage(CAR_BOOT, DOOR_BOOT); + SetDoorDamage(CAR_DOOR_LF, DOOR_FRONT_LEFT); + SetDoorDamage(CAR_DOOR_RF, DOOR_FRONT_RIGHT); + SetDoorDamage(CAR_DOOR_LR, DOOR_REAR_LEFT); + SetDoorDamage(CAR_DOOR_RR, DOOR_REAR_RIGHT); + SpawnFlyingComponent(CAR_WHEEL_LF, COMPGROUP_WHEEL); + atomic = nil; + RwFrameForAllObjects(m_aCarNodes[CAR_WHEEL_LF], GetCurrentAtomicObjectCB, &atomic); + if(atomic) + RpAtomicSetFlags(atomic, 0); + } + + m_fHealth = 0.0f; + m_nBombTimer = 0; + m_bombType = CARBOMB_NONE; + + TheCamera.CamShake(0.7f, GetPosition().x, GetPosition().y, GetPosition().z); + + // kill driver and passengers + if(pDriver){ + CDarkel::RegisterKillByPlayer(pDriver, WEAPONTYPE_EXPLOSION); + if(pDriver->GetPedState() == PED_DRIVING){ + pDriver->SetDead(); + if(!pDriver->IsPlayer()) + pDriver->FlagToDestroyWhenNextProcessed(); + }else + pDriver->SetDie(ANIM_KO_SHOT_FRONT1, 4.0f, 0.0f); + } + for(i = 0; i < m_nNumMaxPassengers; i++){ + if(pPassengers[i]){ + CDarkel::RegisterKillByPlayer(pPassengers[i], WEAPONTYPE_EXPLOSION); + if(pPassengers[i]->GetPedState() == PED_DRIVING){ + pPassengers[i]->SetDead(); + if(!pPassengers[i]->IsPlayer()) + pPassengers[i]->FlagToDestroyWhenNextProcessed(); + }else + pPassengers[i]->SetDie(ANIM_KO_SHOT_FRONT1, 4.0f, 0.0f); + } + } + + bEngineOn = false; + bLightsOn = false; + m_bSirenOrAlarm = false; + bTaxiLight = false; + if(bIsAmbulanceOnDuty){ + bIsAmbulanceOnDuty = false; + CCarCtrl::NumAmbulancesOnDuty--; + } + if(bIsFireTruckOnDuty){ + bIsFireTruckOnDuty = false; + CCarCtrl::NumFiretrucksOnDuty--; + } + ChangeLawEnforcerState(false); + + gFireManager.StartFire(this, culprit, 0.8f, 1); // TODO + CDarkel::RegisterCarBlownUpByPlayer(this); + if(GetModelIndex() == MI_RCBANDIT) + CExplosion::AddExplosion(this, culprit, EXPLOSION_CAR_QUICK, GetPosition(), 0); + else + CExplosion::AddExplosion(this, culprit, EXPLOSION_CAR, GetPosition(), 0); +} + +bool +CAutomobile::SetUpWheelColModel(CColModel *colModel) +{ + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + CColModel *vehColModel = mi->GetColModel(); + + colModel->boundingSphere = vehColModel->boundingSphere; + colModel->boundingBox = vehColModel->boundingBox; + + CMatrix mat; + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF])); + colModel->spheres[0].Set(mi->m_wheelScale, mat.GetPosition(), SURFACE_TIRE, CAR_PIECE_WHEEL_LF); + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LB])); + colModel->spheres[1].Set(mi->m_wheelScale, mat.GetPosition(), SURFACE_TIRE, CAR_PIECE_WHEEL_LR); + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); + colModel->spheres[2].Set(mi->m_wheelScale, mat.GetPosition(), SURFACE_TIRE, CAR_PIECE_WHEEL_RF); + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RB])); + colModel->spheres[3].Set(mi->m_wheelScale, mat.GetPosition(), SURFACE_TIRE, CAR_PIECE_WHEEL_RR); + + if(m_aCarNodes[CAR_WHEEL_LM] != nil && m_aCarNodes[CAR_WHEEL_RM] != nil){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LM])); + colModel->spheres[4].Set(mi->m_wheelScale, mat.GetPosition(), SURFACE_TIRE, CAR_PIECE_WHEEL_RF); + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RM])); + colModel->spheres[5].Set(mi->m_wheelScale, mat.GetPosition(), SURFACE_TIRE, CAR_PIECE_WHEEL_RR); + colModel->numSpheres = 6; + }else + colModel->numSpheres = 4; + + return true; +} + +// this probably isn't used in III yet +void +CAutomobile::BurstTyre(uint8 wheel) +{ + switch(wheel){ + case CAR_PIECE_WHEEL_LF: wheel = VEHWHEEL_FRONT_LEFT; break; + case CAR_PIECE_WHEEL_LR: wheel = VEHWHEEL_REAR_LEFT; break; + case CAR_PIECE_WHEEL_RF: wheel = VEHWHEEL_FRONT_RIGHT; break; + case CAR_PIECE_WHEEL_RR: wheel = VEHWHEEL_REAR_RIGHT; break; + } + + int status = Damage.GetWheelStatus(wheel); + if(status == WHEEL_STATUS_OK){ + Damage.SetWheelStatus(wheel, WHEEL_STATUS_BURST); + + if(m_status == STATUS_SIMPLE){ + m_status = STATUS_PHYSICS; + CCarCtrl::SwitchVehicleToRealPhysics(this); + } + + ApplyMoveForce(GetRight() * CGeneral::GetRandomNumberInRange(-0.3f, 0.3f)); + ApplyTurnForce(GetRight() * CGeneral::GetRandomNumberInRange(-0.3f, 0.3f), GetForward()); + } +} + +bool +CAutomobile::IsRoomForPedToLeaveCar(uint32 component, CVector *doorOffset) +{ + CColPoint colpoint; + CEntity *ent; + colpoint.point = CVector(0.0f, 0.0f, 0.0f); + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + CVector seatPos; + switch(component){ + case CAR_DOOR_RF: + seatPos = mi->m_positions[mi->m_vehicleType == VEHICLE_TYPE_BOAT ? BOAT_POS_FRONTSEAT : CAR_POS_FRONTSEAT]; + break; + case CAR_DOOR_LF: + seatPos = mi->m_positions[mi->m_vehicleType == VEHICLE_TYPE_BOAT ? BOAT_POS_FRONTSEAT : CAR_POS_FRONTSEAT]; + seatPos.x = -seatPos.x; + break; + case CAR_DOOR_RR: + seatPos = mi->m_positions[CAR_POS_BACKSEAT]; + break; + case CAR_DOOR_LR: + seatPos = mi->m_positions[CAR_POS_BACKSEAT]; + seatPos.x = -seatPos.x; + break; + } + seatPos = GetMatrix() * seatPos; + + CVector doorPos = CPed::GetPositionToOpenCarDoor(this, component); + if(doorOffset){ + CVector off = *doorOffset; + if(component == CAR_DOOR_RF || component == CAR_DOOR_RR) + off.x = -off.x; + doorPos += Multiply3x3(GetMatrix(), off); + } + + if(GetUp().z < 0.0f){ + seatPos.z += 0.5f; + doorPos.z += 0.5f; + } + + CVector dist = doorPos - seatPos; + float length = dist.Magnitude(); + CVector pedPos = seatPos + dist*((length+0.6f)/length); + + if(!CWorld::GetIsLineOfSightClear(seatPos, pedPos, true, false, false, true, false, false)) + return false; + if(CWorld::TestSphereAgainstWorld(doorPos, 0.6f, this, true, true, false, true, false, false)) + return false; + if(CWorld::ProcessVerticalLine(doorPos, 1000.0f, colpoint, ent, true, false, false, true, false, false, nil)) + if(colpoint.point.z > doorPos.z && colpoint.point.z < doorPos.z + 0.6f) + return false; + float upperZ = colpoint.point.z; + if(!CWorld::ProcessVerticalLine(doorPos, -1000.0f, colpoint, ent, true, false, false, true, false, false, nil)) + return false; + if(upperZ != 0.0f && upperZ < colpoint.point.z) + return false; + return true; +} + +float +CAutomobile::GetHeightAboveRoad(void) +{ + return m_fHeightAboveRoad; +} + +void +CAutomobile::PlayCarHorn(void) +{ + int r; + + if(m_nCarHornTimer != 0) + return; + + r = CGeneral::GetRandomNumber() & 7; + if(r < 2){ + m_nCarHornTimer = 45; + }else if(r < 4){ + if(pDriver) + pDriver->Say(SOUND_PED_CAR_COLLISION); + m_nCarHornTimer = 45; + }else{ + if(pDriver) + pDriver->Say(SOUND_PED_CAR_COLLISION); + } +} + +void +CAutomobile::PlayHornIfNecessary(void) +{ + if(AutoPilot.m_bSlowedDownBecauseOfPeds || + AutoPilot.m_bSlowedDownBecauseOfCars) + if(!HasCarStoppedBecauseOfLight()) + PlayCarHorn(); +} + + +void +CAutomobile::ResetSuspension(void) +{ + int i; + for(i = 0; i < 4; i++){ + m_aSuspensionSpringRatio[i] = 1.0f; + m_aWheelTimer[i] = 0.0f; + m_aWheelRotation[i] = 0.0f; + m_aWheelState[i] = WHEEL_STATE_NORMAL; + } +} + +void +CAutomobile::SetupSuspensionLines(void) +{ + int i; + CVector posn; + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + CColModel *colModel = mi->GetColModel(); + + // Each suspension line starts at the uppermost wheel position + // and extends down to the lowermost point on the tyre + for(i = 0; i < 4; i++){ + mi->GetWheelPosn(i, posn); + m_aWheelPosition[i] = posn.z; + + // uppermost wheel position + posn.z += pHandling->fSuspensionUpperLimit; + colModel->lines[i].p0 = posn; + + // lowermost wheel position + posn.z += pHandling->fSuspensionLowerLimit - pHandling->fSuspensionUpperLimit; + // lowest point on tyre + posn.z -= mi->m_wheelScale*0.5f; + colModel->lines[i].p1 = posn; + + // this is length of the spring at rest + m_aSuspensionSpringLength[i] = pHandling->fSuspensionUpperLimit - pHandling->fSuspensionLowerLimit; + m_aSuspensionLineLength[i] = colModel->lines[i].p0.z - colModel->lines[i].p1.z; + } + + // Compress spring somewhat to get normal height on road + m_fHeightAboveRoad = -(colModel->lines[0].p0.z + (colModel->lines[0].p1.z - colModel->lines[0].p0.z)* + (1.0f - 1.0f/(8.0f*pHandling->fSuspensionForceLevel))); + for(i = 0; i < 4; i++) + m_aWheelPosition[i] = mi->m_wheelScale*0.5f - m_fHeightAboveRoad; + + // adjust col model to include suspension lines + if(colModel->boundingBox.min.z > colModel->lines[0].p1.z) + colModel->boundingBox.min.z = colModel->lines[0].p1.z; + float radius = Max(colModel->boundingBox.min.Magnitude(), colModel->boundingBox.max.Magnitude()); + if(colModel->boundingSphere.radius < radius) + colModel->boundingSphere.radius = radius; + + if(GetModelIndex() == MI_RCBANDIT){ + colModel->boundingSphere.radius = 2.0f; + for(i = 0; i < colModel->numSpheres; i++) + colModel->spheres[i].radius = 0.3f; + } +} + +// called on police cars +void +CAutomobile::ScanForCrimes(void) +{ + if(FindPlayerVehicle() && FindPlayerVehicle()->IsCar()) + if(FindPlayerVehicle()->IsAlarmOn()) + // if player's alarm is on, increase wanted level + if((FindPlayerVehicle()->GetPosition() - GetPosition()).MagnitudeSqr() < sq(20.0f)) + CWorld::Players[CWorld::PlayerInFocus].m_pPed->SetWantedLevelNoDrop(1); +} + +void +CAutomobile::BlowUpCarsInPath(void) +{ + int i; + + if(m_vecMoveSpeed.Magnitude() > 0.1f) + for(i = 0; i < m_nCollisionRecords; i++) + if(m_aCollisionRecords[i] && + m_aCollisionRecords[i]->IsVehicle() && + m_aCollisionRecords[i]->GetModelIndex() != MI_RHINO && + !m_aCollisionRecords[i]->bRenderScorched) + ((CVehicle*)m_aCollisionRecords[i])->BlowUpCar(this); +} + +bool +CAutomobile::HasCarStoppedBecauseOfLight(void) +{ + int i; + + if(m_status != STATUS_SIMPLE && m_status != STATUS_PHYSICS) + return false; + + if(AutoPilot.m_nCurrentRouteNode && AutoPilot.m_nNextRouteNode){ + CPathNode *curnode = &ThePaths.m_pathNodes[AutoPilot.m_nCurrentRouteNode]; + for(i = 0; i < curnode->numLinks; i++) + if(ThePaths.m_connections[curnode->firstLink + i] == AutoPilot.m_nNextRouteNode) + break; + if(i < curnode->numLinks && + ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[curnode->firstLink + i]].trafficLightType & 3) // TODO + return true; + } + + if(AutoPilot.m_nCurrentRouteNode && AutoPilot.m_nPrevRouteNode){ + CPathNode *curnode = &ThePaths.m_pathNodes[AutoPilot.m_nCurrentRouteNode]; + for(i = 0; i < curnode->numLinks; i++) + if(ThePaths.m_connections[curnode->firstLink + i] == AutoPilot.m_nPrevRouteNode) + break; + if(i < curnode->numLinks && + ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[curnode->firstLink + i]].trafficLightType & 3) // TODO + return true; + } + + return false; +} + +void +CAutomobile::SetBusDoorTimer(uint32 timer, uint8 type) +{ + if(timer < 1000) + timer = 1000; + if(type == 0) + // open and close + m_nBusDoorTimerStart = CTimer::GetTimeInMilliseconds(); + else + // only close + m_nBusDoorTimerStart = CTimer::GetTimeInMilliseconds() - 500; + m_nBusDoorTimerEnd = m_nBusDoorTimerStart + timer; +} + +void +CAutomobile::ProcessAutoBusDoors(void) +{ + if(CTimer::GetTimeInMilliseconds() < m_nBusDoorTimerEnd){ + if(m_nBusDoorTimerEnd != 0 && CTimer::GetTimeInMilliseconds() > m_nBusDoorTimerEnd-500){ + // close door + if(!IsDoorMissing(DOOR_FRONT_LEFT) && (m_nGettingInFlags & 1) == 0){ + if(IsDoorClosed(DOOR_FRONT_LEFT)){ + m_nBusDoorTimerEnd = CTimer::GetTimeInMilliseconds(); + OpenDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT, 0.0f); + }else{ + OpenDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT, + 1.0f - (CTimer::GetTimeInMilliseconds() - (m_nBusDoorTimerEnd-500))/500.0f); + } + } + + if(!IsDoorMissing(DOOR_FRONT_RIGHT) && (m_nGettingInFlags & 4) == 0){ + if(IsDoorClosed(DOOR_FRONT_RIGHT)){ + m_nBusDoorTimerEnd = CTimer::GetTimeInMilliseconds(); + OpenDoor(CAR_DOOR_RF, DOOR_FRONT_RIGHT, 0.0f); + }else{ + OpenDoor(CAR_DOOR_RF, DOOR_FRONT_RIGHT, + 1.0f - (CTimer::GetTimeInMilliseconds() - (m_nBusDoorTimerEnd-500))/500.0f); + } + } + } + }else{ + // ended + if(m_nBusDoorTimerStart){ + if(!IsDoorMissing(DOOR_FRONT_LEFT) && (m_nGettingInFlags & 1) == 0) + OpenDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT, 0.0f); + if(!IsDoorMissing(DOOR_FRONT_RIGHT) && (m_nGettingInFlags & 4) == 0) + OpenDoor(CAR_DOOR_RF, DOOR_FRONT_RIGHT, 0.0f); + m_nBusDoorTimerStart = 0; + m_nBusDoorTimerEnd = 0; + } + } +} + +void +CAutomobile::ProcessSwingingDoor(int32 component, eDoors door) +{ + if(Damage.GetDoorStatus(door) != DOOR_STATUS_SWINGING) + return; + + CMatrix mat(RwFrameGetMatrix(m_aCarNodes[component])); + CVector pos = mat.GetPosition(); + float axes[3] = { 0.0f, 0.0f, 0.0f }; + + Doors[door].Process(this); + axes[Doors[door].m_nAxis] = Doors[door].m_fAngle; + mat.SetRotate(axes[0], axes[1], axes[2]); + mat.Translate(pos); + mat.UpdateRW(); +} + +void +CAutomobile::Fix(void) +{ + int component; + + Damage.ResetDamageStatus(); + + if(pHandling->Flags & HANDLING_NO_DOORS){ + Damage.SetDoorStatus(DOOR_FRONT_LEFT, DOOR_STATUS_MISSING); + Damage.SetDoorStatus(DOOR_FRONT_RIGHT, DOOR_STATUS_MISSING); + Damage.SetDoorStatus(DOOR_REAR_LEFT, DOOR_STATUS_MISSING); + Damage.SetDoorStatus(DOOR_REAR_RIGHT, DOOR_STATUS_MISSING); + } + + bIsDamaged = false; + RpClumpForAllAtomics((RpClump*)m_rwObject, CVehicleModelInfo::HideAllComponentsAtomicCB, (void*)ATOMIC_FLAG_DAM); + + for(component = CAR_BUMP_FRONT; component < NUM_CAR_NODES; component++){ + if(m_aCarNodes[component]){ + CMatrix mat(RwFrameGetMatrix(m_aCarNodes[component])); + mat.SetTranslate(mat.GetPosition()); + mat.UpdateRW(); + } + } +} + +void +CAutomobile::SetupDamageAfterLoad(void) +{ + if(m_aCarNodes[CAR_BUMP_FRONT]) + SetBumperDamage(CAR_BUMP_FRONT, VEHBUMPER_FRONT); + if(m_aCarNodes[CAR_BONNET]) + SetDoorDamage(CAR_BONNET, DOOR_BONNET); + if(m_aCarNodes[CAR_BUMP_REAR]) + SetBumperDamage(CAR_BUMP_REAR, VEHBUMPER_REAR); + if(m_aCarNodes[CAR_BOOT]) + SetDoorDamage(CAR_BOOT, DOOR_BOOT); + if(m_aCarNodes[CAR_DOOR_LF]) + SetDoorDamage(CAR_DOOR_LF, DOOR_FRONT_LEFT); + if(m_aCarNodes[CAR_DOOR_RF]) + SetDoorDamage(CAR_DOOR_RF, DOOR_FRONT_RIGHT); + if(m_aCarNodes[CAR_DOOR_LR]) + SetDoorDamage(CAR_DOOR_LR, DOOR_REAR_LEFT); + if(m_aCarNodes[CAR_DOOR_RR]) + SetDoorDamage(CAR_DOOR_RR, DOOR_REAR_RIGHT); + if(m_aCarNodes[CAR_WING_LF]) + SetPanelDamage(CAR_WING_LF, VEHPANEL_FRONT_LEFT); + if(m_aCarNodes[CAR_WING_RF]) + SetPanelDamage(CAR_WING_RF, VEHPANEL_FRONT_RIGHT); + if(m_aCarNodes[CAR_WING_LR]) + SetPanelDamage(CAR_WING_LR, VEHPANEL_REAR_LEFT); + if(m_aCarNodes[CAR_WING_RR]) + SetPanelDamage(CAR_WING_RR, VEHPANEL_REAR_RIGHT); +} + +RwObject* +GetCurrentAtomicObjectCB(RwObject *object, void *data) +{ + RpAtomic *atomic = (RpAtomic*)object; + assert(RwObjectGetType(object) == rpATOMIC); + if(RpAtomicGetFlags(atomic) & rpATOMICRENDER) + *(RpAtomic**)data = atomic; + return object; +} + +CColPoint aTempPedColPts[32]; // this name doesn't make any sense + +CObject* +CAutomobile::SpawnFlyingComponent(int32 component, uint32 type) +{ + RpAtomic *atomic; + RwFrame *frame; + RwMatrix *matrix; + CObject *obj; + + if(CObject::nNoTempObjects >= NUMTEMPOBJECTS) + return nil; + + atomic = nil; + RwFrameForAllObjects(m_aCarNodes[component], GetCurrentAtomicObjectCB, &atomic); + if(atomic == nil) + return nil; + + obj = new CObject; + if(obj == nil) + return nil; + + if(component == CAR_WINDSCREEN){ + obj->SetModelIndexNoCreate(MI_CAR_BONNET); + }else switch(type){ + case COMPGROUP_BUMPER: + obj->SetModelIndexNoCreate(MI_CAR_BUMPER); + break; + case COMPGROUP_WHEEL: + obj->SetModelIndexNoCreate(MI_CAR_WHEEL); + break; + case COMPGROUP_DOOR: + obj->SetModelIndexNoCreate(MI_CAR_DOOR); + obj->SetCenterOfMass(0.0f, -0.5f, 0.0f); + break; + case COMPGROUP_BONNET: + obj->SetModelIndexNoCreate(MI_CAR_BONNET); + obj->SetCenterOfMass(0.0f, 0.4f, 0.0f); + break; + case COMPGROUP_BOOT: + obj->SetModelIndexNoCreate(MI_CAR_BOOT); + obj->SetCenterOfMass(0.0f, -0.3f, 0.0f); + break; + case COMPGROUP_PANEL: + default: + obj->SetModelIndexNoCreate(MI_CAR_PANEL); + break; + } + + // object needs base model + obj->RefModelInfo(GetModelIndex()); + + // create new atomic + matrix = RwFrameGetLTM(m_aCarNodes[component]); + frame = RwFrameCreate(); + atomic = RpAtomicClone(atomic); + *RwFrameGetMatrix(frame) = *matrix; + RpAtomicSetFrame(atomic, frame); + CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); + obj->AttachToRwObject((RwObject*)atomic); + + // init object + obj->m_fMass = 10.0f; + obj->m_fTurnMass = 25.0f; + obj->m_fAirResistance = 0.97f; + obj->m_fElasticity = 0.1f; + obj->m_fBuoyancy = obj->m_fMass*GRAVITY/0.75f; + obj->ObjectCreatedBy = TEMP_OBJECT; + obj->bIsStatic = false; + obj->bIsPickup = false; + obj->bUseVehicleColours = true; + obj->m_colour1 = m_currentColour1; + obj->m_colour2 = m_currentColour2; + + // life time - the more objects the are, the shorter this one will live + CObject::nNoTempObjects++; + if(CObject::nNoTempObjects > 20) + obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 20000/5.0f; + else if(CObject::nNoTempObjects > 10) + obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 20000/2.0f; + else + obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 20000; + + obj->m_vecMoveSpeed = m_vecMoveSpeed; + if(obj->m_vecMoveSpeed.z > 0.0f){ + obj->m_vecMoveSpeed.z *= 1.5f; + }else if(GetUp().z > 0.0f && + (component == COMPGROUP_BONNET || component == COMPGROUP_BOOT || component == CAR_WINDSCREEN)){ + obj->m_vecMoveSpeed.z *= -1.5f; + obj->m_vecMoveSpeed.z += 0.04f; + }else{ + obj->m_vecMoveSpeed.z *= 0.25f; + } + obj->m_vecMoveSpeed.x *= 0.75f; + obj->m_vecMoveSpeed.y *= 0.75f; + + obj->m_vecTurnSpeed = m_vecTurnSpeed*2.0f; + + // push component away from car + CVector dist = obj->GetPosition() - GetPosition(); + dist.Normalise(); + if(component == COMPGROUP_BONNET || component == COMPGROUP_BOOT || component == CAR_WINDSCREEN){ + // push these up some + dist += GetUp(); + if(GetUp().z > 0.0f){ + // simulate fast upward movement if going fast + float speed = CVector2D(m_vecMoveSpeed).MagnitudeSqr(); + obj->GetPosition() += GetUp()*speed; + } + } + obj->ApplyMoveForce(dist); + + if(type == COMPGROUP_WHEEL){ + obj->m_fTurnMass = 5.0f; + obj->m_vecTurnSpeed.x = 0.5f; + obj->m_fAirResistance = 0.99f; + } + + if(CCollision::ProcessColModels(obj->GetMatrix(), *obj->GetColModel(), + this->GetMatrix(), *this->GetColModel(), + aTempPedColPts, nil, nil) > 0) + obj->m_pCollidingEntity = this; + + if(bRenderScorched) + obj->bRenderScorched = true; + + CWorld::Add(obj); + + return obj; +} + +CObject* +CAutomobile::RemoveBonnetInPedCollision(void) +{ + CObject *obj; + + if(Damage.GetDoorStatus(DOOR_BONNET) != DOOR_STATUS_SWINGING && + Doors[DOOR_BONNET].RetAngleWhenOpen()*0.4f < Doors[DOOR_BONNET].m_fAngle){ + // BUG? why not COMPGROUP_BONNET? + obj = SpawnFlyingComponent(CAR_BONNET, COMPGROUP_DOOR); + // make both doors invisible on car + SetComponentVisibility(m_aCarNodes[CAR_BONNET], ATOMIC_FLAG_NONE); + Damage.SetDoorStatus(DOOR_BONNET, DOOR_STATUS_MISSING); + return obj; + } + return nil; +} + +void +CAutomobile::SetPanelDamage(int32 component, ePanels panel, bool noFlyingComponents) +{ + int status = Damage.GetPanelStatus(panel); + if(m_aCarNodes[component] == nil) + return; + if(status == PANEL_STATUS_SMASHED1){ + // show damaged part + SetComponentVisibility(m_aCarNodes[component], ATOMIC_FLAG_DAM); + }else if(status == PANEL_STATUS_MISSING){ + if(!noFlyingComponents) + SpawnFlyingComponent(component, COMPGROUP_PANEL); + // hide both + SetComponentVisibility(m_aCarNodes[component], ATOMIC_FLAG_NONE); + } +} + +void +CAutomobile::SetBumperDamage(int32 component, ePanels panel, bool noFlyingComponents) +{ + int status = Damage.GetPanelStatus(panel); + if(m_aCarNodes[component] == nil){ + printf("Trying to damage component %d of %s\n", + component, CModelInfo::GetModelInfo(GetModelIndex())->GetName()); + return; + } + if(status == PANEL_STATUS_SMASHED1){ + // show damaged part + SetComponentVisibility(m_aCarNodes[component], ATOMIC_FLAG_DAM); + }else if(status == PANEL_STATUS_MISSING){ + if(!noFlyingComponents) + SpawnFlyingComponent(component, COMPGROUP_BUMPER); + // hide both + SetComponentVisibility(m_aCarNodes[component], ATOMIC_FLAG_NONE); + } +} + +void +CAutomobile::SetDoorDamage(int32 component, eDoors door, bool noFlyingComponents) +{ + int status = Damage.GetDoorStatus(door); + if(m_aCarNodes[component] == nil){ + printf("Trying to damage component %d of %s\n", + component, CModelInfo::GetModelInfo(GetModelIndex())->GetName()); + return; + } + + if(door == DOOR_BOOT && status == DOOR_STATUS_SWINGING && pHandling->Flags & HANDLING_NOSWING_BOOT){ + Damage.SetDoorStatus(DOOR_BOOT, DOOR_STATUS_MISSING); + status = DOOR_STATUS_MISSING; + } + + if(status == DOOR_STATUS_SMASHED){ + // show damaged part + SetComponentVisibility(m_aCarNodes[component], ATOMIC_FLAG_DAM); + }else if(status == DOOR_STATUS_SWINGING){ + // turn off angle cull for swinging doors + RwFrameForAllObjects(m_aCarNodes[component], CVehicleModelInfo::SetAtomicFlagCB, (void*)ATOMIC_FLAG_NOCULL); + }else if(status == DOOR_STATUS_MISSING){ + if(!noFlyingComponents){ + if(door == DOOR_BONNET) + SpawnFlyingComponent(component, COMPGROUP_BONNET); + else if(door == DOOR_BOOT) + SpawnFlyingComponent(component, COMPGROUP_BOOT); + else + SpawnFlyingComponent(component, COMPGROUP_DOOR); + } + // hide both + SetComponentVisibility(m_aCarNodes[component], ATOMIC_FLAG_NONE); + } +} + + +static RwObject* +SetVehicleAtomicVisibilityCB(RwObject *object, void *data) +{ + uint32 flags = (uint32)(uintptr)data; + RpAtomic *atomic = (RpAtomic*)object; + if((CVisibilityPlugins::GetAtomicId(atomic) & (ATOMIC_FLAG_OK|ATOMIC_FLAG_DAM)) == flags) + RpAtomicSetFlags(atomic, rpATOMICRENDER); + else + RpAtomicSetFlags(atomic, 0); + return object; +} + +void +CAutomobile::SetComponentVisibility(RwFrame *frame, uint32 flags) +{ + HideAllComps(); + bIsDamaged = true; + RwFrameForAllObjects(frame, SetVehicleAtomicVisibilityCB, (void*)flags); +} + +void +CAutomobile::SetupModelNodes(void) +{ + int i; + for(i = 0; i < NUM_CAR_NODES; i++) + m_aCarNodes[i] = nil; + CClumpModelInfo::FillFrameArray((RpClump*)m_rwObject, m_aCarNodes); +} + +void +CAutomobile::SetTaxiLight(bool light) +{ + bTaxiLight = light; +} + +bool +CAutomobile::GetAllWheelsOffGround(void) +{ + return m_nDriveWheelsOnGround == 0; +} + +void +CAutomobile::HideAllComps(void) +{ + // empty +} + +void +CAutomobile::ShowAllComps(void) +{ + // empty +} + +void +CAutomobile::ReduceHornCounter(void) +{ + if(m_nCarHornTimer != 0) + m_nCarHornTimer--; +} + +void +CAutomobile::SetAllTaxiLights(bool set) +{ + m_sAllTaxiLights = set; +} + +class CAutomobile_ : public CAutomobile +{ +public: + void ctor(int32 id, uint8 CreatedBy) { ::new (this) CAutomobile(id, CreatedBy); } + void dtor() { CAutomobile::~CAutomobile(); } + void SetModelIndex_(uint32 id) { CAutomobile::SetModelIndex(id); } + void ProcessControl_(void) { CAutomobile::ProcessControl(); } + void Teleport_(CVector v) { CAutomobile::Teleport(v); } + void PreRender_(void) { CAutomobile::PreRender(); } + void Render_(void) { CAutomobile::Render(); } + + int32 ProcessEntityCollision_(CEntity *ent, CColPoint *colpoints){ return CAutomobile::ProcessEntityCollision(ent, colpoints); } + + void ProcessControlInputs_(uint8 pad) { CAutomobile::ProcessControlInputs(pad); } + void GetComponentWorldPosition_(int32 component, CVector &pos) { CAutomobile::GetComponentWorldPosition(component, pos); } + bool IsComponentPresent_(int32 component) { return CAutomobile::IsComponentPresent(component); } + void SetComponentRotation_(int32 component, CVector rotation) { CAutomobile::SetComponentRotation(component, rotation); } + void OpenDoor_(int32 component, eDoors door, float ratio) { CAutomobile::OpenDoor(component, door, ratio); } + void ProcessOpenDoor_(uint32 component, uint32 anim, float time) { CAutomobile::ProcessOpenDoor(component, anim, time); } + bool IsDoorReady_(eDoors door) { return CAutomobile::IsDoorReady(door); } + bool IsDoorFullyOpen_(eDoors door) { return CAutomobile::IsDoorFullyOpen(door); } + bool IsDoorClosed_(eDoors door) { return CAutomobile::IsDoorClosed(door); } + bool IsDoorMissing_(eDoors door) { return CAutomobile::IsDoorMissing(door); } + void RemoveRefsToVehicle_(CEntity *ent) { CAutomobile::RemoveRefsToVehicle(ent); } + void BlowUpCar_(CEntity *ent) { CAutomobile::BlowUpCar(ent); } + bool SetUpWheelColModel_(CColModel *colModel) { return CAutomobile::SetUpWheelColModel(colModel); } + void BurstTyre_(uint8 tyre) { CAutomobile::BurstTyre(tyre); } + bool IsRoomForPedToLeaveCar_(uint32 door, CVector *pos) { return CAutomobile::IsRoomForPedToLeaveCar(door, pos); } + float GetHeightAboveRoad_(void) { return CAutomobile::GetHeightAboveRoad(); } + void PlayCarHorn_(void) { CAutomobile::PlayCarHorn(); } +}; + +STARTPATCHES + InjectHook(0x52C6B0, &CAutomobile_::ctor, PATCH_JUMP); + InjectHook(0x52D170, &CAutomobile_::dtor, PATCH_JUMP); + InjectHook(0x52D190, &CAutomobile_::SetModelIndex_, PATCH_JUMP); + InjectHook(0x531470, &CAutomobile_::ProcessControl_, PATCH_JUMP); + InjectHook(0x535180, &CAutomobile_::Teleport_, PATCH_JUMP); + InjectHook(0x539EA0, &CAutomobile_::Render_, PATCH_JUMP); + InjectHook(0x535B40, &CAutomobile_::PreRender_, PATCH_JUMP); + InjectHook(0x53B270, &CAutomobile_::ProcessEntityCollision_, PATCH_JUMP); + InjectHook(0x53B660, &CAutomobile_::ProcessControlInputs_, PATCH_JUMP); + InjectHook(0x52E5F0, &CAutomobile_::GetComponentWorldPosition_, PATCH_JUMP); + InjectHook(0x52E660, &CAutomobile_::IsComponentPresent_, PATCH_JUMP); + InjectHook(0x52E680, &CAutomobile_::SetComponentRotation_, PATCH_JUMP); + InjectHook(0x52E750, &CAutomobile_::OpenDoor_, PATCH_JUMP); + InjectHook(0x52EF10, &CAutomobile_::IsDoorReady_, PATCH_JUMP); + InjectHook(0x52EF90, &CAutomobile_::IsDoorFullyOpen_, PATCH_JUMP); + InjectHook(0x52EFD0, &CAutomobile_::IsDoorClosed_, PATCH_JUMP); + InjectHook(0x52F000, &CAutomobile_::IsDoorMissing_, PATCH_JUMP); + InjectHook(0x53BF40, &CAutomobile_::RemoveRefsToVehicle_, PATCH_JUMP); + InjectHook(0x53BC60, &CAutomobile_::BlowUpCar_, PATCH_JUMP); + InjectHook(0x53BF70, &CAutomobile_::SetUpWheelColModel_, PATCH_JUMP); + InjectHook(0x53C0E0, &CAutomobile_::BurstTyre_, PATCH_JUMP); + InjectHook(0x53C5B0, &CAutomobile_::IsRoomForPedToLeaveCar_, PATCH_JUMP); + InjectHook(0x437690, &CAutomobile_::GetHeightAboveRoad_, PATCH_JUMP); + InjectHook(0x53C450, &CAutomobile_::PlayCarHorn_, PATCH_JUMP); + InjectHook(0x53E090, &CAutomobile::PlaceOnRoadProperly, PATCH_JUMP); + InjectHook(0x52F030, &CAutomobile::dmgDrawCarCollidingParticles, PATCH_JUMP); + InjectHook(0x535450, &CAutomobile::AddDamagedVehicleParticles, PATCH_JUMP); + InjectHook(0x5357D0, &CAutomobile::AddWheelDirtAndWater, PATCH_JUMP); + InjectHook(0x5353A0, &CAutomobile::ResetSuspension, PATCH_JUMP); + InjectHook(0x52D210, &CAutomobile::SetupSuspensionLines, PATCH_JUMP); + InjectHook(0x53E000, &CAutomobile::BlowUpCarsInPath, PATCH_JUMP); + InjectHook(0x42E220, &CAutomobile::HasCarStoppedBecauseOfLight, PATCH_JUMP); + InjectHook(0x53D320, &CAutomobile::SetBusDoorTimer, PATCH_JUMP); + InjectHook(0x53D370, &CAutomobile::ProcessAutoBusDoors, PATCH_JUMP); + InjectHook(0x535250, &CAutomobile::ProcessSwingingDoor, PATCH_JUMP); + InjectHook(0x53C240, &CAutomobile::Fix, PATCH_JUMP); + InjectHook(0x53C310, &CAutomobile::SetupDamageAfterLoad, PATCH_JUMP); + InjectHook(0x530300, &CAutomobile::SpawnFlyingComponent, PATCH_JUMP); + InjectHook(0x535320, &CAutomobile::RemoveBonnetInPedCollision, PATCH_JUMP); + InjectHook(0x5301A0, &CAutomobile::SetPanelDamage, PATCH_JUMP); + InjectHook(0x530120, &CAutomobile::SetBumperDamage, PATCH_JUMP); + InjectHook(0x530200, &CAutomobile::SetDoorDamage, PATCH_JUMP); + InjectHook(0x5300E0, &CAutomobile::SetComponentVisibility, PATCH_JUMP); + InjectHook(0x52D1B0, &CAutomobile::SetupModelNodes, PATCH_JUMP); + InjectHook(0x53C420, &CAutomobile::SetTaxiLight, PATCH_JUMP); + InjectHook(0x53BC40, &CAutomobile::GetAllWheelsOffGround, PATCH_JUMP); + InjectHook(0x5308C0, &CAutomobile::ReduceHornCounter, PATCH_JUMP); + InjectHook(0x53C440, &CAutomobile::SetAllTaxiLights, PATCH_JUMP); +ENDPATCHES diff --git a/src/vehicles/Boat.cpp b/src/vehicles/Boat.cpp index f614b78f..2b5ff567 100644 --- a/src/vehicles/Boat.cpp +++ b/src/vehicles/Boat.cpp @@ -61,7 +61,7 @@ float CBoat::IsVertexAffectedByWake(CVector vecVertex, CBoat *pBoat) float fDist = vecDist.MagnitudeSqr(); if ( fDist < SQR(fMaxDist) ) - return 1.0f - min(fRangeMult * Sqrt(fDist / SQR(fMaxDist)) + (WAKE_LIFETIME - pBoat->m_afWakePointLifeTime[i]) * fTimeMult, 1.0f); + return 1.0f - Min(fRangeMult * Sqrt(fDist / SQR(fMaxDist)) + (WAKE_LIFETIME - pBoat->m_afWakePointLifeTime[i]) * fTimeMult, 1.0f); } return 0.0f; diff --git a/src/vehicles/Heli.cpp b/src/vehicles/Heli.cpp index 9b1a651d..1c242f0e 100644 --- a/src/vehicles/Heli.cpp +++ b/src/vehicles/Heli.cpp @@ -1038,8 +1038,16 @@ void CHeli::ActivateHeli(bool activate) { ScriptHeliOn = activate; } class CHeli_ : public CHeli { public: +<<<<<<< HEAD +<<<<<<< HEAD void ctor(int32 id, uint8 CreatedBy) { ::new (this) CHeli(id, CreatedBy); } void dtor(void) { CHeli::~CHeli(); } +======= + void dtor(void) { this->~CHeli(); } +>>>>>>> More audio ped +======= + void dtor(void) { CHeli::~CHeli(); } +>>>>>>> fix }; STARTPATCHES diff --git a/src/vehicles/Heli.cpp.autosave b/src/vehicles/Heli.cpp.autosave new file mode 100644 index 00000000..9b1a651d --- /dev/null +++ b/src/vehicles/Heli.cpp.autosave @@ -0,0 +1,1055 @@ +#include "common.h" +#include "main.h" +#include "patcher.h" +#include "General.h" +#include "Darkel.h" +#include "Stats.h" +#include "SurfaceTable.h" +#include "ModelIndices.h" +#include "Streaming.h" +#include "Camera.h" +#include "VisibilityPlugins.h" +#include "ZoneCull.h" +#include "Particle.h" +#include "Shadows.h" +#include "Coronas.h" +#include "Explosion.h" +#include "TimeCycle.h" +#include "TempColModels.h" +#include "World.h" +#include "WaterLevel.h" +#include "PlayerPed.h" +#include "Object.h" +#include "HandlingMgr.h" +#include "Heli.h" + +enum +{ + HELI_STATUS_HOVER, + HELI_STATUS_CHASE_PLAYER, + HELI_STATUS_FLY_AWAY, + HELI_STATUS_SHOT_DOWN, + HELI_STATUS_HOVER2, +}; + +CHeli **CHeli::pHelis = (CHeli**)0x72CF50; +int16 &CHeli::NumRandomHelis = *(int16*)0x95CCAA; +uint32 &CHeli::TestForNewRandomHelisTimer = *(uint32*)0x8F1A7C; +int16 CHeli::NumScriptHelis; // unused +bool &CHeli::CatalinaHeliOn = *(bool*)0x95CD85; +bool &CHeli::CatalinaHasBeenShotDown = *(bool*)0x95CD56; +bool &CHeli::ScriptHeliOn = *(bool*)0x95CD43; + +CHeli::CHeli(int32 id, uint8 CreatedBy) + : CVehicle(CreatedBy) +{ + int i; + + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); + m_vehType = VEHICLE_TYPE_HELI; + pHandling = mod_HandlingManager.GetHandlingData((eHandlingId)mi->m_handlingId); + SetModelIndex(id); + m_heliStatus = HELI_STATUS_HOVER; + m_pathState = 0; + + m_fMass = 100000000.0f; + m_fTurnMass = 100000000.0f; + m_fAirResistance = 0.9994f; + m_fElasticity = 0.05f; + + m_nHeliId = 0; + m_fRotorRotation = 0.0f; + m_nBulletDamage = 0; + m_fAngularSpeed = 0.0f; + m_fRotation = 0.0f; + m_nSearchLightTimer = CTimer::GetTimeInMilliseconds(); + for(i = 0; i < 6; i++){ + m_aSearchLightHistoryX[i] = 0.0f; + m_aSearchLightHistoryY[i] = 0.0f; + } + + for(i = 0; i < 8; i++) + m_fHeliDustZ[i] = -50.0f; + + m_nPoliceShoutTimer = CTimer::GetTimeInMilliseconds(); + m_status = STATUS_HELI; + m_bTestRight = true; + m_fTargetOffset = 0.0f; + m_fSearchLightX = m_fSearchLightY = 0.0f; +} + +void +CHeli::SetModelIndex(uint32 id) +{ + int i; + + CVehicle::SetModelIndex(id); + for(i = 0; i < NUM_HELI_NODES; i++) + m_aHeliNodes[i] = nil; + CClumpModelInfo::FillFrameArray(GetClump(), m_aHeliNodes); +} + +static float CatalinaTargetX[7] = { -478.0, -677.0, -907.0, -1095.0, -1152.0, -1161.0, -1161.0 }; +static float CatalinaTargetY[7] = { 227.0, 206.0, 210.0, 242.0, 278.0, 341.0, 341.0 }; +static float CatalinaTargetZ[7] = { 77.0, 66.0, 60.0, 53.0, 51.0, 46.0, 30.0 }; +static float DamPathX[6] = { -1191.0, -1176.0, -1128.0, -1072.0, -1007.0, -971.0 }; +static float DamPathY[6] = { 350.0, 388.0, 429.0, 447.0, 449.0, 416.0 }; +static float DamPathZ[6] = { 42.0, 37.0, 28.0, 28.0, 31.0, 33.0 }; +static float ShortPathX[4] = { -974.0, -1036.0, -1112.0, -1173.0 }; +static float ShortPathY[4] = { 340.0, 312.0, 317.0, 294.0 }; +static float ShortPathZ[4] = { 41.0, 38.0, 32.0, 39.0 }; +static float LongPathX[7] = { -934.0, -905.0, -906.0, -1063.0, -1204.0, -1233.0, -1207.0 }; +static float LongPathY[7] = { 371.0, 362.0, 488.0, 548.0, 451.0, 346.0, 308.0 }; +static float LongPathZ[7] = { 57.0, 90.0, 105.0, 100.0, 81.0, 79.0, 70.0 }; + +static int PathPoint; + +void +CHeli::ProcessControl(void) +{ + int i; + + if(gbModelViewer) + return; + + // Find target + CVector target(0.0f, 0.0f, 0.0f); + CVector2D vTargetDist; + if(m_heliType == HELI_TYPE_CATALINA && m_heliStatus != HELI_STATUS_SHOT_DOWN){ + switch(m_pathState){ + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + target.x = CatalinaTargetX[m_pathState]; + target.y = CatalinaTargetY[m_pathState]; + target.z = CatalinaTargetZ[m_pathState]; + if((target - GetPosition()).Magnitude() < 9.0f) + m_pathState++; + break; + case 6: + target.x = CatalinaTargetX[m_pathState]; + target.y = CatalinaTargetY[m_pathState]; + target.z = CatalinaTargetZ[m_pathState]; + if(GetPosition().z > 31.55f) + break; + m_pathState = 7; + GetPosition().z = 31.55f; + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + break; + case 7: + GetPosition().z = 31.55f; + target = GetPosition(); + break; + + + // Take off + case 8: + target.x = GetPosition().x; + target.y = GetPosition().y; + target.z = 74.0f; + if(GetPosition().z < 40.0f) + break; + PathPoint = 2; + m_pathState = 9; + break; + // Circle around dam + case 9: + target.x = DamPathX[PathPoint]; + target.y = DamPathY[PathPoint]; + target.z = DamPathZ[PathPoint]; + if((target - GetPosition()).Magnitude() < 9.0f){ + PathPoint++; + if(PathPoint >= 6){ + m_pathState = 10; + PathPoint = 0; + } + } + break; + case 10: + target.x = ShortPathX[PathPoint]; + target.y = ShortPathY[PathPoint]; + target.z = ShortPathZ[PathPoint]; + if((target - GetPosition()).Magnitude() < 9.0f){ + PathPoint++; + if(PathPoint >= 3){ + m_pathState = 9; + PathPoint = 1; + } + } + break; + // how do we get here? + case 11: + target.x = LongPathX[PathPoint]; + target.y = LongPathY[PathPoint]; + target.z = LongPathZ[PathPoint]; + if((target - GetPosition()).Magnitude() < 9.0f){ + PathPoint++; + if(PathPoint >= 7){ + m_pathState = 9; + PathPoint = 0; + } + } + break; + + + // Fly away + case 12: + target.x = GetPosition().x; + target.y = GetPosition().y; + target.z = 200.0f; + break; + } + + vTargetDist = target - GetPosition(); + m_fTargetZ = target.z; + if(m_pathState == 6){ + GetPosition().x = GetPosition().x*0.99f + target.x*0.01f; + GetPosition().y = GetPosition().y*0.99f + target.y*0.01f; + } + }else{ + vTargetDist = FindPlayerCoors() - GetPosition(); + m_fTargetZ = FindPlayerCoors().z; + + // Heli flies away to (0, 0) + if(m_heliStatus == HELI_STATUS_FLY_AWAY && GetPosition().z > 20.0f){ + vTargetDist.x = 0.0f - GetPosition().x; + vTargetDist.y = 0.0f - GetPosition().y; + } + + float groundZ; + switch(m_heliStatus){ + case HELI_STATUS_HOVER: + groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, 1000.0f, nil); + m_fTargetZ = max(groundZ, m_fTargetZ) + 8.0f; + break; + case HELI_STATUS_SHOT_DOWN: + groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, 1000.0f, nil); + m_fTargetZ = max(groundZ, m_fTargetZ) + 8.0f + m_fTargetOffset; + break; + case HELI_STATUS_HOVER2: + groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, 1000.0f, nil); + m_fTargetZ = max(groundZ, m_fTargetZ) + 8.0f + m_fTargetOffset; + break; + default: + groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, 1000.0f, nil); + m_fTargetZ = max(groundZ, m_fTargetZ) + 12.0f; + break; + } + + // Move up if too low + if(GetPosition().z - 2.0f < groundZ && m_heliStatus != HELI_STATUS_SHOT_DOWN) + m_vecMoveSpeed.z += CTimer::GetTimeStep()*0.01f; + m_vecMoveSpeed.z = clamp(m_vecMoveSpeed.z, -0.3f, 0.3f); + } + + float fTargetDist = vTargetDist.Magnitude(); + + switch(m_heliStatus){ + case HELI_STATUS_HOVER: + case HELI_STATUS_HOVER2:{ + float targetHeight; + if(m_heliType == HELI_TYPE_CATALINA) + targetHeight = 8.0f; + else + targetHeight = 40.0f - m_nHeliId*10.0f; + if(fTargetDist > targetHeight) + m_heliStatus = HELI_STATUS_CHASE_PLAYER; + } + // fall through, BUG? + case HELI_STATUS_CHASE_PLAYER:{ + float targetHeight; + if(m_heliType == HELI_TYPE_CATALINA) + targetHeight = 4.0f; + else + targetHeight = 30.0f - m_nHeliId*7.5f; + if(fTargetDist < 1.0f || + fTargetDist < targetHeight && CWorld::GetIsLineOfSightClear(GetPosition(), FindPlayerCoors(), true, false, false, false, false, false)) + m_heliStatus = HELI_STATUS_HOVER; + } + } + + // Find xy speed + float speed; + if(fTargetDist > 100.0f) + speed = 1.0f; + else if(fTargetDist > 75.0f) + speed = 0.7f; + else + speed = 0.4f; + if(m_heliStatus == HELI_STATUS_HOVER || m_heliStatus == HELI_STATUS_HOVER2 || m_heliStatus == HELI_STATUS_SHOT_DOWN) + speed = 0.0f; + + if(fTargetDist != 0.0f) + vTargetDist /= fTargetDist; + else + vTargetDist.x = 1.0f; + CVector2D targetSpeed = vTargetDist * speed; + + if(m_heliStatus == HELI_STATUS_HOVER2 || m_heliStatus == HELI_STATUS_SHOT_DOWN){ + bool force = !!((CTimer::GetFrameCounter() + m_randomSeed) & 8); + if(m_bTestRight){ + if(force || CWorld::TestSphereAgainstWorld(GetPosition() + 4.0f*GetRight(), 2.0f, this, true, false, false, false, false, false) == nil){ + if(m_heliStatus == HELI_STATUS_SHOT_DOWN){ + m_fTargetOffset -= CTimer::GetTimeStep()*0.05f; + targetSpeed.x -= -vTargetDist.x*0.15f; + targetSpeed.y -= vTargetDist.y*0.15f; + }else{ + targetSpeed.x -= -vTargetDist.x*0.05f; + targetSpeed.y -= vTargetDist.y*0.05f; + } + }else{ + m_bTestRight = false; + if(m_heliStatus == HELI_STATUS_HOVER2) + m_fTargetOffset += 5.0f; + else + m_fTargetOffset -= 5.0f; + } + }else{ + if(force || CWorld::TestSphereAgainstWorld(GetPosition() - 4.0f*GetRight(), 2.0f, this, true, false, false, false, false, false) == nil){ + if(m_heliStatus == HELI_STATUS_SHOT_DOWN){ + m_fTargetOffset -= CTimer::GetTimeStep()*0.05f; + targetSpeed.x += -vTargetDist.x*0.15f; + targetSpeed.y += vTargetDist.y*0.15f; + }else{ + targetSpeed.x += -vTargetDist.x*0.05f; + targetSpeed.y += vTargetDist.y*0.05f; + } + }else{ + m_bTestRight = true; + if(m_heliStatus == HELI_STATUS_HOVER2) + m_fTargetOffset += 5.0f; + else + m_fTargetOffset -= 5.0f; + } + } + + if(m_fTargetOffset > 30.0f) + m_fTargetOffset = 30.0f; + + if(m_heliStatus == HELI_STATUS_SHOT_DOWN && force){ + if(CWorld::TestSphereAgainstWorld(GetPosition() + 1.5f*GetForward(), 2.0f, this, true, false, false, false, false, false) || + CWorld::TestSphereAgainstWorld(GetPosition() - 1.5f*GetForward(), 2.0f, this, true, false, false, false, false, false)) + m_nExplosionTimer = CTimer::GetPreviousTimeInMilliseconds(); + } + }else + if(m_fTargetOffset >= 2.0f) + m_fTargetOffset -= 2.0f; + + if(m_heliType == HELI_TYPE_CATALINA) + if(m_pathState == 9 || m_pathState == 11 || m_pathState == 10){ + float f = Pow(0.997f, CTimer::GetTimeStep()); + m_vecMoveSpeed.x *= f; + m_vecMoveSpeed.y *= f; + } + + CVector2D speedDir = targetSpeed - m_vecMoveSpeed; + float speedDiff = speedDir.Magnitude(); + if(speedDiff != 0.0f) + speedDir /= speedDiff; + else + speedDir.x = 1.0f; + float speedInc = CTimer::GetTimeStep()*0.002f; + if(speedDiff < speedInc){ + m_vecMoveSpeed.x = targetSpeed.x; + m_vecMoveSpeed.y = targetSpeed.y; + }else{ + m_vecMoveSpeed.x += speedDir.x*speedInc; + m_vecMoveSpeed.y += speedDir.y*speedInc; + } + GetPosition().x += m_vecMoveSpeed.x*CTimer::GetTimeStep(); + GetPosition().y += m_vecMoveSpeed.y*CTimer::GetTimeStep(); + + // Find z target + if(m_heliStatus == HELI_STATUS_FLY_AWAY) + m_fTargetZ = 1000.0f; + if((CTimer::GetTimeInMilliseconds() + 800*m_nHeliId) & 0x800) + m_fTargetZ += 2.0f; + m_fTargetZ += m_nHeliId*5.0f; + + // Find z speed + float targetSpeedZ = (m_fTargetZ - GetPosition().z)*0.01f; + float speedDiffZ = targetSpeedZ - m_vecMoveSpeed.z; + float speedIncZ = CTimer::GetTimeStep()*0.001f; + if(m_heliStatus == HELI_STATUS_FLY_AWAY) + speedIncZ *= 1.5f; + if(Abs(speedDiffZ) < speedIncZ) + m_vecMoveSpeed.z = targetSpeedZ; + else if(speedDiffZ < 0.0f) + m_vecMoveSpeed.z -= speedIncZ; + else + m_vecMoveSpeed.z += speedIncZ*1.5f; + GetPosition().z += m_vecMoveSpeed.z*CTimer::GetTimeStep(); + + // Find angular speed + float targetAngularSpeed; + m_fAngularSpeed *= Pow(0.995f, CTimer::GetTimeStep()); + if(fTargetDist < 8.0f) + targetAngularSpeed = 0.0f; + else{ + float rotationDiff = CGeneral::GetATanOfXY(vTargetDist.x, vTargetDist.y) - m_fRotation; + while(rotationDiff < -3.14f) rotationDiff += 6.28f; + while(rotationDiff > 3.14f) rotationDiff -= 6.28f; + if(Abs(rotationDiff) > 0.4f){ + if(rotationDiff < 0.0f) + targetAngularSpeed = -0.2f; + else + targetAngularSpeed = 0.2f; + }else + targetAngularSpeed = 0.0f; + } + float angularSpeedDiff = targetAngularSpeed - m_fAngularSpeed; + float angularSpeedInc = CTimer::GetTimeStep()*0.0001f; + if(Abs(angularSpeedDiff) < angularSpeedInc) + m_fAngularSpeed = targetAngularSpeed; + else if(angularSpeedDiff < 0.0f) + m_fAngularSpeed -= angularSpeedInc; + else + m_fAngularSpeed += angularSpeedInc; + m_fRotation += m_fAngularSpeed * CTimer::GetTimeStep(); + + // Set matrix + CVector up(3.0f*m_vecMoveSpeed.x, 3.0f*m_vecMoveSpeed.y, 1.0f); + up.Normalise(); + CVector fwd(-Cos(m_fRotation), -Sin(m_fRotation), 0.0f); // not really forward + CVector right = CrossProduct(up, fwd); + fwd = CrossProduct(up, right); + GetRight() = right; + GetForward() = fwd; + GetUp() = up; + + // Search light and shooting + if(m_heliStatus == HELI_STATUS_FLY_AWAY || m_heliType == HELI_TYPE_CATALINA || CCullZones::PlayerNoRain()) + m_fSearchLightIntensity = 0.0f; + else{ + // Update search light history once every 1000ms + int timeDiff = CTimer::GetTimeInMilliseconds() - m_nSearchLightTimer; + while(timeDiff > 1000){ + for(i = 5; i > 0; i--){ + m_aSearchLightHistoryX[i] = m_aSearchLightHistoryX[i-1]; + m_aSearchLightHistoryY[i] = m_aSearchLightHistoryY[i-1]; + } + m_aSearchLightHistoryX[0] = FindPlayerCoors().x + FindPlayerSpeed().x*50.0f*(m_nHeliId+2); + m_aSearchLightHistoryY[0] = FindPlayerCoors().y + FindPlayerSpeed().y*50.0f*(m_nHeliId+2); + + timeDiff -= 1000; + m_nSearchLightTimer += 1000; + } + assert(timeDiff <= 1000); + float f1 = timeDiff/1000.0f; + float f2 = 1.0f - f1; + m_fSearchLightX = m_aSearchLightHistoryX[m_nHeliId+2]*f2 + m_aSearchLightHistoryX[m_nHeliId+2-1]*f1; + m_fSearchLightY = m_aSearchLightHistoryY[m_nHeliId+2]*f2 + m_aSearchLightHistoryY[m_nHeliId+2-1]*f1; + + float searchLightDist = (CVector2D(m_fSearchLightX, m_fSearchLightY) - GetPosition()).Magnitude(); + if(searchLightDist > 60.0f) + m_fSearchLightIntensity = 0.0f; + else if(searchLightDist < 40.0f) + m_fSearchLightIntensity = 1.0f; + else + m_fSearchLightIntensity = 1.0f - (40.0f-searchLightDist)/40.0f; + + if(m_fSearchLightIntensity < 0.9f || sq(FindPlayerCoors().x-m_fSearchLightX) + sq(FindPlayerCoors().y-m_fSearchLightY) > sq(7.0f)) + m_nShootTimer = CTimer::GetTimeInMilliseconds(); + else if(CTimer::GetTimeInMilliseconds() > m_nPoliceShoutTimer){ + DMAudio.PlayOneShot(m_audioEntityId, SOUND_PED_HELI_PLAYER_FOUND, 0.0f); + m_nPoliceShoutTimer = CTimer::GetTimeInMilliseconds() + 4500 + (CGeneral::GetRandomNumber()&0xFFF); + } + + // Shoot + int shootTimeout; + if(m_heliType == HELI_TYPE_RANDOM){ + switch(FindPlayerPed()->m_pWanted->m_nWantedLevel){ + case 0: + case 1: + case 2: shootTimeout = 999999; break; + case 3: shootTimeout = 10000; break; + case 4: shootTimeout = 5000; break; + case 5: shootTimeout = 3500; break; + case 6: shootTimeout = 2000; break; + } + if(CCullZones::NoPolice()) + shootTimeout /= 2; + }else + shootTimeout = 1500; + + if(FindPlayerPed()->m_pWanted->IsIgnored()) + m_nShootTimer = CTimer::GetTimeInMilliseconds(); + else{ + // Check if line of sight is clear + if(CTimer::GetTimeInMilliseconds() > m_nShootTimer + shootTimeout && + CTimer::GetPreviousTimeInMilliseconds() <= m_nShootTimer + shootTimeout){ + if(CWorld::GetIsLineOfSightClear(GetPosition(), FindPlayerCoors(), true, false, false, false, false, false)){ + if(m_heliStatus == HELI_STATUS_HOVER2) + m_heliStatus = HELI_STATUS_HOVER; + }else{ + m_nShootTimer = CTimer::GetTimeInMilliseconds(); + if(m_heliStatus == HELI_STATUS_HOVER) + m_heliStatus = HELI_STATUS_HOVER2; + } + } + + // Shoot! + if(CTimer::GetTimeInMilliseconds() > m_nShootTimer + shootTimeout && + CTimer::GetTimeInMilliseconds() > m_nLastShotTime + 200){ + CVector shotTarget = FindPlayerCoors(); + // some inaccuracy + shotTarget.x += ((CGeneral::GetRandomNumber()&0xFF)-128)/50.0f; + shotTarget.y += ((CGeneral::GetRandomNumber()&0xFF)-128)/50.0f; + CVector direction = FindPlayerCoors() - GetPosition(); + direction.Normalise(); + shotTarget += 3.0f*direction; + CVector shotSource = GetPosition(); + shotSource += 3.0f*direction; + FireOneInstantHitRound(&shotSource, &shotTarget, 20); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); + m_nLastShotTime = CTimer::GetTimeInMilliseconds(); + } + } + } + + // Drop Catalina's bombs + if(m_heliType == HELI_TYPE_CATALINA && m_pathState > 8 && (CTimer::GetTimeInMilliseconds()>>9) != (CTimer::GetPreviousTimeInMilliseconds()>>9)){ + CVector bombPos = GetPosition() - 60.0f*m_vecMoveSpeed; + if(sq(FindPlayerCoors().x-bombPos.x) + sq(FindPlayerCoors().y-bombPos.y) < sq(35.0f)){ + bool found; + float groundZ = CWorld::FindGroundZFor3DCoord(bombPos.x, bombPos.y, bombPos.z, &found); + float waterZ; + if(!CWaterLevel::GetWaterLevelNoWaves(bombPos.x, bombPos.y, bombPos.z, &waterZ)) + waterZ = 0.0f; + if(groundZ > waterZ){ + bombPos.z = groundZ + 2.0f; + CExplosion::AddExplosion(nil, this, EXPLOSION_HELI_BOMB, bombPos, 0); + }else{ + bombPos.z = waterZ; + CVector dir; + for(i = 0; i < 16; i++){ + dir.x = ((CGeneral::GetRandomNumber()&0xFF)-127)*0.001f; + dir.y = ((CGeneral::GetRandomNumber()&0xFF)-127)*0.001f; + dir.z = 0.5f; + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, bombPos, dir, nil, 0.2f); + } + } + } + } + + RemoveAndAdd(); + bIsInSafePosition = true; + GetMatrix().UpdateRW(); + UpdateRwFrame(); +} + +void +CHeli::PreRender(void) +{ + float angle; + uint8 i; + CColPoint point; + CEntity *entity; + uint8 r, g, b; + float testLowZ = FindPlayerCoors().z - 10.0f; + float radius = (GetPosition().z - FindPlayerCoors().z - 10.0f - 1.0f) * 0.3f + 10.0f; + int frm = CTimer::GetFrameCounter() & 7; + + i = 0; + for(angle = 0.0f; angle < TWOPI; angle += TWOPI/32){ + CVector pos(radius*Cos(angle), radius*Sin(angle), 0.0f); + CVector dir = pos*0.01f; + pos += GetPosition(); + + if(CWorld::ProcessVerticalLine(pos, testLowZ, point, entity, true, false, false, false, true, false, nil)) + m_fHeliDustZ[frm] = point.point.z; + else + m_fHeliDustZ[frm] = -101.0f; + + switch(point.surfaceB){ + default: + case SURFACE_TARMAC: + r = 10; + g = 10; + b = 10; + break; + case SURFACE_GRASS: + r = 10; + g = 6; + b = 3; + break; + case SURFACE_DIRT: + r = 10; + g = 8; + b = 7; + break; + case SURFACE_DIRTTRACK: + r = 10; + g = 6; + b = 3; + break; + } + RwRGBA col = { r, g, b, 32 }; + pos.z = m_fHeliDustZ[(i - (i&3))/4]; // advance every 4 iterations, why not just /4? + if(pos.z > -200.0f && GetPosition().z - pos.z < 20.0f) + CParticle::AddParticle(PARTICLE_HELI_DUST, pos, dir, nil, 0.0f, col); + i++; + } +} + +void +CHeli::Render(void) +{ + CMatrix mat; + CVector pos; + + mat.Attach(RwFrameGetMatrix(m_aHeliNodes[HELI_TOPROTOR])); + pos = mat.GetPosition(); + mat.SetRotateZ(m_fRotorRotation); + mat.Translate(pos); + mat.UpdateRW(); + + m_fRotorRotation += 3.14f/6.5f; + if(m_fRotorRotation > 6.28f) + m_fRotorRotation -= 6.28f; + + mat.Attach(RwFrameGetMatrix(m_aHeliNodes[HELI_BACKROTOR])); + pos = mat.GetPosition(); + mat.SetRotateX(m_fRotorRotation); + mat.Translate(pos); + mat.UpdateRW(); + + CEntity::Render(); +} + +void +CHeli::PreRenderAlways(void) +{ + CVector shadowPos(m_fSearchLightX, m_fSearchLightY, GetPosition().z); + if(m_fSearchLightIntensity > 0.0f){ + CShadows::StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &shadowPos, + 6.0f, 0.0f, 0.0f, -6.0f, + 80*m_fSearchLightIntensity, 80*m_fSearchLightIntensity, 80*m_fSearchLightIntensity, 80*m_fSearchLightIntensity, + 50.0f, true, 1.0f); + + CVector front = GetMatrix() * CVector(0.0f, 7.0f, 0.0f); + CVector toPlayer = FindPlayerCoors() - front; + toPlayer.Normalise(); + float intensity = m_fSearchLightIntensity*sq(CTimeCycle::GetSpriteBrightness()); + if(DotProduct(toPlayer, TheCamera.GetForward()) < -0.8f) + CCoronas::RegisterCorona((uintptr)this, 255*intensity, 255*intensity, 255*intensity, 255, + front, 10.0f, 60.0f, CCoronas::TYPE_STAR, + CCoronas::FLARE_HEADLIGHTS, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + else + CCoronas::RegisterCorona((uintptr)this, 200*intensity, 200*intensity, 200*intensity, 255, + front, 8.0f, 60.0f, CCoronas::TYPE_STAR, + CCoronas::FLARE_HEADLIGHTS, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + } + + CVector back = GetMatrix() * CVector(0.0f, -9.0f, 0.0f); + if(CTimer::GetTimeInMilliseconds() & 0x100) + CCoronas::RegisterCorona((uintptr)this + 2, 255, 0, 0, 255, + back, 1.0f, 60.0f, CCoronas::TYPE_STAR, + CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + else + CCoronas::RegisterCorona((uintptr)this + 2, 0, 0, 0, 255, + back, 1.0f, 60.0f, CCoronas::TYPE_STAR, + CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); +} + +RwObject* +GetHeliAtomicObjectCB(RwObject *object, void *data) +{ + RpAtomic *atomic = (RpAtomic*)object; + assert(RwObjectGetType(object) == rpATOMIC); + if(RpAtomicGetFlags(atomic) & rpATOMICRENDER) + *(RpAtomic**)data = atomic; + return object; +} + +CObject* +CHeli::SpawnFlyingComponent(int32 component) +{ + RpAtomic *atomic; + RwFrame *frame; + RwMatrix *matrix; + CObject *obj; + + if(m_aHeliNodes[component] == nil) + return nil; + + atomic = nil; + RwFrameForAllObjects(m_aHeliNodes[component], GetHeliAtomicObjectCB, &atomic); + if(atomic == nil) + return nil; + + obj = new CObject; + if(obj == nil) + return nil; + + obj->SetModelIndexNoCreate(MI_CAR_WHEEL); + // object needs base model + obj->RefModelInfo(GetModelIndex()); + + // create new atomic + matrix = RwFrameGetLTM(m_aHeliNodes[component]); + frame = RwFrameCreate(); + atomic = RpAtomicClone(atomic); + *RwFrameGetMatrix(frame) = *matrix; + RpAtomicSetFrame(atomic, frame); + CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); + obj->AttachToRwObject((RwObject*)atomic); + + // init object + obj->m_fMass = 10.0f; + obj->m_fTurnMass = 25.0f; + obj->m_fAirResistance = 0.99f; + obj->m_fElasticity = 0.1f; + obj->m_fBuoyancy = obj->m_fMass*GRAVITY/0.75f; + obj->ObjectCreatedBy = TEMP_OBJECT; + obj->bIsStatic = false; + obj->bIsPickup = false; + + // life time + CObject::nNoTempObjects++; + if(component == HELI_TOPROTOR) + obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 1000; + else + obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 3000; + + obj->m_vecMoveSpeed = m_vecMoveSpeed; + if(obj->m_vecMoveSpeed.z > 0.0f) + obj->m_vecMoveSpeed.z = 0.3f; + else + obj->m_vecMoveSpeed.z = 0.0f; + + obj->m_vecTurnSpeed = m_vecTurnSpeed*2.0f; + + if(component == HELI_BACKROTOR) + obj->m_vecTurnSpeed.x = 0.5f; + else if(component == HELI_TOPROTOR || component == HELI_TOPKNOT) + obj->m_vecTurnSpeed.z = 0.5f; + else + obj->m_vecTurnSpeed.y = 0.5f; + + obj->bRenderScorched = true; + + CWorld::Add(obj); + + atomic = nil; + RwFrameForAllObjects(m_aHeliNodes[component], GetHeliAtomicObjectCB, &atomic); + if(atomic) + RpAtomicSetFlags(atomic, 0); + + return obj; +} + + + +void +CHeli::InitHelis(void) +{ + int i; + + NumRandomHelis = 0; + TestForNewRandomHelisTimer = 0; + NumScriptHelis = 0; + CatalinaHeliOn = false; + ScriptHeliOn = false; + for(i = 0; i < NUM_HELIS; i++) + pHelis[i] = nil; + + ((CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_ESCAPE))->SetColModel(&CTempColModels::ms_colModelPed1); + ((CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_CHOPPER))->SetColModel(&CTempColModels::ms_colModelPed1); +} + +CHeli* +GenerateHeli(bool catalina) +{ + CHeli *heli; + CVector heliPos; + int i; + + if(catalina) + heli = new CHeli(MI_ESCAPE, PERMANENT_VEHICLE); + else + heli = new CHeli(MI_CHOPPER, PERMANENT_VEHICLE); + + if(catalina) + heliPos = CVector(-224.0f, 201.0f, 83.0f); + else{ + heliPos = FindPlayerCoors(); + float angle = (float)(CGeneral::GetRandomNumber() & 0xFF)/0xFF * 6.28f; + heliPos.x += 250.0f*Sin(angle); + heliPos.y += 250.0f*Cos(angle); + if(heliPos.x < -2000.0f || heliPos.x > 2000.0f || heliPos.y < -2000.0f || heliPos.y > 2000.0f){ + // directly above player + heliPos.x -= 250.0f*Sin(angle); + heliPos.y -= 250.0f*Cos(angle); + } + heliPos.z += 50.0f; + } + heli->GetMatrix().SetTranslate(heliPos); + if(catalina) + heli->GetMatrix().SetRotateZOnly(DEGTORAD(270.0f)); // game actually uses 3.14 here + + heli->m_status = STATUS_ABANDONED; + + int id = -1; + bool found = false; + while(!found){ + id++; + found = true; + for(i = 0; i < 4; i++) + if(CHeli::pHelis[i] && CHeli::pHelis[i]->m_nHeliId == id) + found = false; + } + heli->m_nHeliId = id; + + CWorld::Add(heli); + + return heli; +} + +void +CHeli::UpdateHelis(void) +{ + int i, j; + + // Spawn new police helis + int numHelisRequired = FindPlayerPed()->m_pWanted->NumOfHelisRequired(); + if(CStreaming::HasModelLoaded(MI_CHOPPER) && CTimer::GetTimeInMilliseconds() > TestForNewRandomHelisTimer){ + // Spawn a police heli + TestForNewRandomHelisTimer = CTimer::GetTimeInMilliseconds() + 15000; + if(NumRandomHelis < numHelisRequired){ + NumRandomHelis++; + CHeli *heli = GenerateHeli(false); + heli->m_heliType = HELI_TYPE_RANDOM; + if(pHelis[HELI_RANDOM0] == nil) + pHelis[HELI_RANDOM0] = heli; + else if(pHelis[HELI_RANDOM1] == nil) + pHelis[HELI_RANDOM1] = heli; + else + assert(0 && "too many helis"); + } + } + + // Handle script heli + if(ScriptHeliOn){ + if(CStreaming::HasModelLoaded(MI_CHOPPER) && pHelis[HELI_SCRIPT] == nil){ + pHelis[HELI_SCRIPT] = GenerateHeli(false); + pHelis[HELI_SCRIPT]->m_heliType = HELI_TYPE_SCRIPT; + }else + CStreaming::RequestModel(MI_CHOPPER, 0); + }else{ + if(pHelis[HELI_SCRIPT]) + pHelis[HELI_SCRIPT]->m_heliStatus = HELI_STATUS_FLY_AWAY; + } + + // Handle Catalina's heli + if(CatalinaHeliOn){ + if(CStreaming::HasModelLoaded(MI_ESCAPE) && pHelis[HELI_CATALINA] == nil){ + pHelis[HELI_CATALINA] = GenerateHeli(true); + pHelis[HELI_CATALINA]->m_heliType = HELI_TYPE_CATALINA; + }else + CStreaming::RequestModel(MI_ESCAPE, STREAMFLAGS_DONT_REMOVE); + }else{ + if(pHelis[HELI_CATALINA]) + pHelis[HELI_CATALINA]->m_heliStatus = HELI_STATUS_FLY_AWAY; + } + + // Delete helis that we no longer need + for(i = 0; i < NUM_HELIS; i++) + if(pHelis[i] && pHelis[i]->m_heliStatus == HELI_STATUS_FLY_AWAY && pHelis[i]->GetPosition().z > 150.0f){ + CWorld::Remove(pHelis[i]); + delete pHelis[i]; + pHelis[i] = nil; + if(i != HELI_SCRIPT && i != HELI_CATALINA) + NumRandomHelis--; + } + + // Handle explosions + for(i = 0; i < NUM_HELIS; i++){ + if(pHelis[i] && pHelis[i]->m_heliStatus == HELI_STATUS_SHOT_DOWN && CTimer::GetTimeInMilliseconds() > pHelis[i]->m_nExplosionTimer){ + // Second part of explosion + static int nFrameGen; + CRGBA colors[8]; + + TheCamera.CamShake(0.7f, pHelis[i]->GetPosition().x, pHelis[i]->GetPosition().y, pHelis[i]->GetPosition().z); + + colors[0] = CRGBA(0, 0, 0, 255); + colors[1] = CRGBA(224, 230, 238, 255); + colors[2] = CRGBA(0, 0, 0, 255); + colors[3] = CRGBA(0, 0, 0, 255); + colors[4] = CRGBA(66, 162, 252, 255); + colors[5] = CRGBA(0, 0, 0, 255); + colors[6] = CRGBA(0, 0, 0, 255); + colors[7] = CRGBA(0, 0, 0, 255); + + CVector pos = pHelis[i]->GetPosition(); + CVector dir; + for(j = 0; j < 40; j++){ + dir.x = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f); + dir.y = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f); + dir.z = CGeneral::GetRandomNumberInRange(0.0f, 2.0f); + int rotSpeed = CGeneral::GetRandomNumberInRange(10, 30); + if(CGeneral::GetRandomNumber() & 1) + rotSpeed = -rotSpeed; + int f = ++nFrameGen & 3; + CParticle::AddParticle(PARTICLE_HELI_DEBRIS, pos, dir, + nil, CGeneral::GetRandomNumberInRange(0.1f, 1.0f), + colors[nFrameGen], rotSpeed, 0, f, 0); + } + + CExplosion::AddExplosion(nil, nil, EXPLOSION_HELI, pos, 0); + + pHelis[i]->SpawnFlyingComponent(HELI_SKID_LEFT); + pHelis[i]->SpawnFlyingComponent(HELI_SKID_RIGHT); + pHelis[i]->SpawnFlyingComponent(HELI_TOPROTOR); + + CDarkel::RegisterCarBlownUpByPlayer(pHelis[i]); + CWorld::Remove(pHelis[i]); + delete pHelis[i]; + pHelis[i] = nil; + if(i != HELI_SCRIPT && i != HELI_CATALINA) + NumRandomHelis--; + if(i == HELI_CATALINA) + CatalinaHasBeenShotDown = true; + + CStats::HelisDestroyed++; + CStats::PeopleKilledByOthers += 2; + CStats::PedsKilledOfThisType[PEDTYPE_COP] += 2; + CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 250; + pos = CWorld::Players[CWorld::PlayerInFocus].m_pPed->GetPosition(); + CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->RegisterCrime_Immediately(CRIME_SHOOT_HELI, + pos, i + 19843, false); + + TestForNewRandomHelisTimer = CTimer::GetTimeInMilliseconds() + 50000; + }else if(pHelis[i] && pHelis[i]->m_heliStatus == HELI_STATUS_SHOT_DOWN && CTimer::GetTimeInMilliseconds()+7000 > pHelis[i]->m_nExplosionTimer){ + // First part of explosion + if(CTimer::GetPreviousTimeInMilliseconds()+7000 < pHelis[i]->m_nExplosionTimer){ + pHelis[i]->SpawnFlyingComponent(HELI_BACKROTOR); + pHelis[i]->SpawnFlyingComponent(HELI_TAIL); + pHelis[i]->m_fAngularSpeed *= -2.5f; + pHelis[i]->bRenderScorched = true; + + TheCamera.CamShake(0.4f, pHelis[i]->GetPosition().x, pHelis[i]->GetPosition().y, pHelis[i]->GetPosition().z); + + CVector pos = pHelis[i]->GetPosition() - 2.5f*pHelis[i]->GetUp(); + CExplosion::AddExplosion(nil, nil, EXPLOSION_HELI, pos, 0); + }else + pHelis[i]->m_fAngularSpeed *= 1.03f; + } + } + + // Find police helis to remove + for(i = 0; i < 2; i++) + if(pHelis[i] && pHelis[i]->m_heliStatus != HELI_STATUS_FLY_AWAY){ + if(numHelisRequired > 0) + numHelisRequired--; + else + pHelis[i]->m_heliStatus = HELI_STATUS_FLY_AWAY; + } + + // Remove all helis if in a tunnel + if(FindPlayerCoors().z < - 2.0f) + for(i = 0; i < NUM_HELIS; i++) + if(pHelis[i] && pHelis[i]->m_heliStatus != HELI_STATUS_SHOT_DOWN) + pHelis[i]->m_heliStatus = HELI_STATUS_FLY_AWAY; +} + +void +CHeli::SpecialHeliPreRender(void) +{ + int i; + for(i = 0; i < NUM_HELIS; i++) + if(pHelis[i]) + pHelis[i]->PreRenderAlways(); +} + +bool +CHeli::TestRocketCollision(CVector *rocketPos) +{ + int i; + bool hit = false; + + for(i = 0; i < NUM_HELIS; i++){ + if(pHelis[i] && !pHelis[i]->bExplosionProof && (*rocketPos - pHelis[i]->GetPosition()).MagnitudeSqr() < sq(8.0f)){ + pHelis[i]->m_fAngularSpeed = (CGeneral::GetRandomNumber() < RAND_MAX/2) ? 0.05f : -0.05f; + pHelis[i]->m_heliStatus = HELI_STATUS_SHOT_DOWN; + pHelis[i]->m_nExplosionTimer = CTimer::GetTimeInMilliseconds() + 10000; + hit = true; + } + } + return hit; +} + +bool +CHeli::TestBulletCollision(CVector *line0, CVector *line1, CVector *bulletPos, int32 damage) +{ + int i; + bool hit = false; + + for(i = 0; i < NUM_HELIS; i++) + if(pHelis[i] && !pHelis[i]->bBulletProof && CCollision::DistToLine(line0, line1, &pHelis[i]->GetPosition()) < 5.0f){ + // Find bullet position + float distToHeli = (pHelis[i]->GetPosition() - *line0).Magnitude(); + CVector line = (*line1 - *line0); + float lineLength = line.Magnitude(); + *bulletPos = *line0 + line*max(1.0f, distToHeli-5.0f); + + pHelis[i]->m_nBulletDamage += damage; + + if(pHelis[i]->m_heliType == HELI_CATALINA && pHelis[i]->m_nBulletDamage > 400 || + pHelis[i]->m_heliType != HELI_CATALINA && pHelis[i]->m_nBulletDamage > 700){ + pHelis[i]->m_fAngularSpeed = (CGeneral::GetRandomNumber() < RAND_MAX/2) ? 0.05f : -0.05f; + pHelis[i]->m_heliStatus = HELI_STATUS_SHOT_DOWN; + pHelis[i]->m_nExplosionTimer = CTimer::GetTimeInMilliseconds() + 10000; + } + + hit = true; + } + return hit; +} + +void CHeli::StartCatalinaFlyBy(void) +{ + CatalinaHeliOn = true; + CatalinaHasBeenShotDown = false; +} + +void +CHeli::RemoveCatalinaHeli(void) +{ + CatalinaHeliOn = false; + if(pHelis[HELI_CATALINA]){ + CWorld::Remove(pHelis[HELI_CATALINA]); + delete pHelis[HELI_CATALINA]; + pHelis[HELI_CATALINA] = nil; + } +} + +CHeli *CHeli::FindPointerToCatalinasHeli(void) { return pHelis[HELI_CATALINA]; } +void CHeli::CatalinaTakeOff(void) { pHelis[HELI_CATALINA]->m_pathState = 8; } +void CHeli::MakeCatalinaHeliFlyAway(void) { pHelis[HELI_CATALINA]->m_pathState = 12; } +bool CHeli::HasCatalinaBeenShotDown(void) { return CatalinaHasBeenShotDown; } + +void CHeli::ActivateHeli(bool activate) { ScriptHeliOn = activate; } + + +class CHeli_ : public CHeli +{ +public: + void ctor(int32 id, uint8 CreatedBy) { ::new (this) CHeli(id, CreatedBy); } + void dtor(void) { CHeli::~CHeli(); } +}; + +STARTPATCHES + InjectHook(0x547220, &CHeli_::ctor, PATCH_JUMP); + InjectHook(0x5474A0, &CHeli_::dtor, PATCH_JUMP); + InjectHook(0x54AE50, &CHeli::SpawnFlyingComponent, PATCH_JUMP); + InjectHook(0x549970, CHeli::InitHelis, PATCH_JUMP); + InjectHook(0x5499F0, CHeli::UpdateHelis, PATCH_JUMP); + InjectHook(0x54AE10, CHeli::SpecialHeliPreRender, PATCH_JUMP); + InjectHook(0x54AA30, CHeli::TestRocketCollision, PATCH_JUMP); + InjectHook(0x54AB30, CHeli::TestBulletCollision, PATCH_JUMP); + InjectHook(0x54A640, GenerateHeli, PATCH_JUMP); +ENDPATCHES diff --git a/src/vehicles/Plane.cpp b/src/vehicles/Plane.cpp index 775cf572..771f36e1 100644 --- a/src/vehicles/Plane.cpp +++ b/src/vehicles/Plane.cpp @@ -968,8 +968,16 @@ bool CPlane::HasDropOffCesnaBeenShotDown(void) { return DropOffCesnaMissionStatu class CPlane_ : public CPlane { public: +<<<<<<< HEAD +<<<<<<< HEAD void ctor(int32 id, uint8 CreatedBy) { ::new (this) CPlane(id, CreatedBy); } void dtor(void) { CPlane::~CPlane(); } +======= + void dtor(void) { this->~CPlane(); } +>>>>>>> More audio ped +======= + void dtor(void) { CPlane::~CPlane(); } +>>>>>>> fix }; STARTPATCHES diff --git a/src/vehicles/Plane.cpp.autosave b/src/vehicles/Plane.cpp.autosave new file mode 100644 index 00000000..775cf572 --- /dev/null +++ b/src/vehicles/Plane.cpp.autosave @@ -0,0 +1,985 @@ +#include "common.h" +#include "main.h" +#include "patcher.h" +#include "General.h" +#include "ModelIndices.h" +#include "FileMgr.h" +#include "Streaming.h" +#include "Replay.h" +#include "Camera.h" +#include "Coronas.h" +#include "Particle.h" +#include "Explosion.h" +#include "World.h" +#include "HandlingMgr.h" +#include "Plane.h" + +CPlaneNode *&pPathNodes = *(CPlaneNode**)0x8F1B68; +CPlaneNode *&pPath2Nodes = *(CPlaneNode**)0x885B8C; +CPlaneNode *&pPath3Nodes = *(CPlaneNode**)0x885B78; +CPlaneNode *&pPath4Nodes = *(CPlaneNode**)0x885AD8; +int32 &NumPathNodes = *(int32*)0x8F2BE4; +int32 &NumPath2Nodes = *(int32*)0x941498; +int32 &NumPath3Nodes = *(int32*)0x9414D8; +int32 &NumPath4Nodes = *(int32*)0x9412C8; +float &TotalLengthOfFlightPath = *(float*)0x8F2C6C; +float &TotalLengthOfFlightPath2 = *(float*)0x64CFBC; +float &TotalLengthOfFlightPath3 = *(float*)0x64CFD0; +float &TotalLengthOfFlightPath4 = *(float*)0x64CFDC; +float &TotalDurationOfFlightPath = *(float*)0x64CFB8; +float &TotalDurationOfFlightPath2 = *(float*)0x64CFC0; +float &TotalDurationOfFlightPath3 = *(float*)0x64CFD4; +float &TotalDurationOfFlightPath4 = *(float*)0x64CFE0; +float &LandingPoint = *(float*)0x8F2C7C; +float &TakeOffPoint = *(float*)0x8E28A4; +CPlaneInterpolationLine *aPlaneLineBits = (CPlaneInterpolationLine*)0x734168; //[6] + +float *PlanePathPosition = (float*)0x8F5FC8; //[3] +float *OldPlanePathPosition = (float*)0x8F5FBC; //[3] +float *PlanePathSpeed = (float*)0x941538; //[3] +float *PlanePath2Position = (float*)0x64CFC4; //[3] +float &PlanePath3Position = *(float*)0x64CFD8; +float &PlanePath4Position = *(float*)0x64CFE4; +float *PlanePath2Speed = (float*)0x8F1A54; //[3] +float &PlanePath3Speed = *(float*)0x8F1A94; +float &PlanePath4Speed = *(float*)0x8F1AFC; + + +enum +{ + CESNA_STATUS_NONE, // doesn't even exist + CESNA_STATUS_FLYING, + CESNA_STATUS_DESTROYED, + CESNA_STATUS_LANDED, +}; + +int32 &CesnaMissionStatus = *(int32*)0x64CFE8; +int32 &CesnaMissionStartTime = *(int32*)0x64CFEC; +CPlane *&pDrugRunCesna = *(CPlane**)0x8F5F80; +int32 &DropOffCesnaMissionStatus = *(int32*)0x64CFF0; +int32 &DropOffCesnaMissionStartTime = *(int32*)0x64CFF4; +CPlane *&pDropOffCesna = *(CPlane**)0x8E2A38; + + +CPlane::CPlane(int32 id, uint8 CreatedBy) + : CVehicle(CreatedBy) +{ + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); + m_vehType = VEHICLE_TYPE_PLANE; + pHandling = mod_HandlingManager.GetHandlingData((eHandlingId)mi->m_handlingId); + SetModelIndex(id); + + m_fMass = 100000000.0f; + m_fTurnMass = 100000000.0f; + m_fAirResistance = 0.9994f; + m_fElasticity = 0.05f; + + bUsesCollision = false; + m_bHasBeenHit = false; + m_bIsDrugRunCesna = false; + m_bIsDropOffCesna = false; + + m_status = STATUS_PLANE; + bIsBIGBuilding = true; + m_level = LEVEL_NONE; +} + +CPlane::~CPlane() +{ + DeleteRwObject(); +} + +void +CPlane::SetModelIndex(uint32 id) +{ + CVehicle::SetModelIndex(id); +} + +void +CPlane::DeleteRwObject(void) +{ + if(m_rwObject && RwObjectGetType(m_rwObject) == rpATOMIC){ + m_matrix.Detach(); + if(RwObjectGetType(m_rwObject) == rpATOMIC){ // useless check + RwFrame *f = RpAtomicGetFrame((RpAtomic*)m_rwObject); + RpAtomicDestroy((RpAtomic*)m_rwObject); + RwFrameDestroy(f); + } + m_rwObject = nil; + } + CEntity::DeleteRwObject(); +} + +// There's a LOT of copy and paste in here. Maybe this could be refactored somehow +void +CPlane::ProcessControl(void) +{ + int i; + CVector pos; + + // Explosion + if(m_bHasBeenHit){ + // BUG: since this is all based on frames, you can skip the explosion processing when you go into the menu + if(GetModelIndex() == MI_AIRTRAIN){ + int frm = CTimer::GetFrameCounter() - m_nFrameWhenHit; + if(frm == 20){ + static int nFrameGen; + CRGBA colors[8]; + + CExplosion::AddExplosion(nil, FindPlayerPed(), EXPLOSION_HELI, GetMatrix() * CVector(0.0f, 0.0f, 0.0f), 0); + + colors[0] = CRGBA(0, 0, 0, 255); + colors[1] = CRGBA(224, 230, 238, 255); + colors[2] = CRGBA(224, 230, 238, 255); + colors[3] = CRGBA(0, 0, 0, 255); + colors[4] = CRGBA(224, 230, 238, 255); + colors[5] = CRGBA(0, 0, 0, 255); + colors[6] = CRGBA(0, 0, 0, 255); + colors[7] = CRGBA(224, 230, 238, 255); + + CVector dir; + for(i = 0; i < 40; i++){ + dir.x = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f); + dir.y = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f); + dir.z = CGeneral::GetRandomNumberInRange(0.0f, 2.0f); + int rotSpeed = CGeneral::GetRandomNumberInRange(10, 30); + if(CGeneral::GetRandomNumber() & 1) + rotSpeed = -rotSpeed; + int f = ++nFrameGen & 3; + CParticle::AddParticle(PARTICLE_HELI_DEBRIS, GetMatrix() * CVector(0.0f, 0.0f, 0.0f), dir, + nil, CGeneral::GetRandomNumberInRange(0.1f, 1.0f), + colors[nFrameGen], rotSpeed, 0, f, 0); + } + } + if(frm >= 40 && frm <= 80 && frm & 1){ + if(frm & 1){ + pos.x = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.2f; + pos.z = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.2f; + pos.y = frm - 40; + pos = GetMatrix() * pos; + }else{ + pos.x = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.2f; + pos.z = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.2f; + pos.y = 40 - frm; + pos = GetMatrix() * pos; + } + CExplosion::AddExplosion(nil, FindPlayerPed(), EXPLOSION_HELI, pos, 0); + } + if(frm == 60) + bRenderScorched = true; + if(frm == 82){ + TheCamera.SetFadeColour(255, 255, 255); + TheCamera.Fade(0.0f, FADE_OUT); + TheCamera.ProcessFade(); + TheCamera.Fade(1.0f, FADE_IN); + FlagToDestroyWhenNextProcessed(); + } + }else{ + int frm = CTimer::GetFrameCounter() - m_nFrameWhenHit; + if(frm == 20){ + static int nFrameGen; + CRGBA colors[8]; + + CExplosion::AddExplosion(nil, FindPlayerPed(), EXPLOSION_HELI, GetMatrix() * CVector(0.0f, 0.0f, 0.0f), 0); + + colors[0] = CRGBA(0, 0, 0, 255); + colors[1] = CRGBA(224, 230, 238, 255); + colors[2] = CRGBA(224, 230, 238, 255); + colors[3] = CRGBA(0, 0, 0, 255); + colors[4] = CRGBA(252, 66, 66, 255); + colors[5] = CRGBA(0, 0, 0, 255); + colors[6] = CRGBA(0, 0, 0, 255); + colors[7] = CRGBA(252, 66, 66, 255); + + for(i = 0; i < 40; i++){ + int rotSpeed = CGeneral::GetRandomNumberInRange(30.0f, 20.0f); + if(CGeneral::GetRandomNumber() & 1) + rotSpeed = -rotSpeed; + int f = ++nFrameGen & 3; + CParticle::AddParticle(PARTICLE_HELI_DEBRIS, GetMatrix() * CVector(0.0f, 0.0f, 0.0f), + CVector(CGeneral::GetRandomNumberInRange(-2.0f, 2.0f), + CGeneral::GetRandomNumberInRange(-2.0f, 2.0f), + CGeneral::GetRandomNumberInRange(0.0f, 2.0f)), + nil, CGeneral::GetRandomNumberInRange(0.1f, 1.0f), + colors[nFrameGen], rotSpeed, 0, f, 0); + } + } + if(frm >= 40 && frm <= 60 && frm & 1){ + if(frm & 1){ + pos.x = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.1f; + pos.z = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.1f; + pos.y = (frm - 40)*0.3f; + pos = GetMatrix() * pos; + }else{ + pos.x = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.1f; + pos.z = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.1f; + pos.y = (40 - frm)*0.3f; + pos = GetMatrix() * pos; + } + CExplosion::AddExplosion(nil, FindPlayerPed(), EXPLOSION_HELI, pos, 0); + } + if(frm == 30) + bRenderScorched = true; + if(frm == 61){ + TheCamera.SetFadeColour(200, 200, 200); + TheCamera.Fade(0.0f, FADE_OUT); + TheCamera.ProcessFade(); + TheCamera.Fade(1.0f, FADE_IN); + if(m_bIsDrugRunCesna){ + CesnaMissionStatus = CESNA_STATUS_DESTROYED; + pDrugRunCesna = nil; + } + if(m_bIsDropOffCesna){ + DropOffCesnaMissionStatus = CESNA_STATUS_DESTROYED; + pDropOffCesna = nil; + } + FlagToDestroyWhenNextProcessed(); + } + } + } + + // Update plane position and speed + if(GetModelIndex() == MI_AIRTRAIN || !m_isFarAway || ((CTimer::GetFrameCounter() + m_randomSeed) & 7) == 0){ + if(GetModelIndex() == MI_AIRTRAIN){ + float pathPositionRear = PlanePathPosition[m_nPlaneId] - 30.0f; + if(pathPositionRear < 0.0f) + pathPositionRear += TotalLengthOfFlightPath; + float pathPosition = pathPositionRear + 30.0f; + + float pitch = 0.0f; + float distSinceTakeOff = pathPosition - TakeOffPoint; + if(distSinceTakeOff <= 0.0f && distSinceTakeOff > -70.0f){ + // shortly before take off + pitch = 1.0f - distSinceTakeOff/-70.0f; + }else if(distSinceTakeOff >= 0.0f && distSinceTakeOff < 100.0f){ + // shortly after take off + pitch = 1.0f - distSinceTakeOff/100.0f; + } + + float distSinceLanding = pathPosition - LandingPoint; + if(distSinceLanding <= 0.0f && distSinceLanding > -200.0f){ + // shortly before landing + pitch = 1.0f - distSinceLanding/-200.0f; + }else if(distSinceLanding >= 0.0f && distSinceLanding < 70.0f){ + // shortly after landing + pitch = 1.0f - distSinceLanding/70.0f; + } + + + // Advance current node to appropriate position + float pos1, pos2; + int nextTrackNode = m_nCurPathNode + 1; + pos1 = pPathNodes[m_nCurPathNode].t; + if(nextTrackNode < NumPathNodes) + pos2 = pPathNodes[nextTrackNode].t; + else{ + nextTrackNode = 0; + pos2 = TotalLengthOfFlightPath; + } + while(pathPositionRear < pos1 || pathPositionRear > pos2){ + m_nCurPathNode = (m_nCurPathNode+1) % NumPathNodes; + nextTrackNode = m_nCurPathNode + 1; + pos1 = pPathNodes[m_nCurPathNode].t; + if(nextTrackNode < NumPathNodes) + pos2 = pPathNodes[nextTrackNode].t; + else{ + nextTrackNode = 0; + pos2 = TotalLengthOfFlightPath; + } + } + bool bothOnGround = pPathNodes[m_nCurPathNode].bOnGround && pPathNodes[nextTrackNode].bOnGround; + if(PlanePathPosition[m_nPlaneId] >= LandingPoint && OldPlanePathPosition[m_nPlaneId] < LandingPoint) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_PLANE_ON_GROUND, 0.0f); + float dist = pPathNodes[nextTrackNode].t - pPathNodes[m_nCurPathNode].t; + if(dist < 0.0f) + dist += TotalLengthOfFlightPath; + float f = (pathPositionRear - pPathNodes[m_nCurPathNode].t)/dist; + CVector posRear = (1.0f - f)*pPathNodes[m_nCurPathNode].p + f*pPathNodes[nextTrackNode].p; + + // Same for the front + float pathPositionFront = pathPositionRear + 60.0f; + if(pathPositionFront > TotalLengthOfFlightPath) + pathPositionFront -= TotalLengthOfFlightPath; + int curPathNodeFront = m_nCurPathNode; + int nextPathNodeFront = curPathNodeFront + 1; + pos1 = pPathNodes[curPathNodeFront].t; + if(nextPathNodeFront < NumPathNodes) + pos2 = pPathNodes[nextPathNodeFront].t; + else{ + nextPathNodeFront = 0; + pos2 = TotalLengthOfFlightPath; + } + while(pathPositionFront < pos1 || pathPositionFront > pos2){ + curPathNodeFront = (curPathNodeFront+1) % NumPathNodes; + nextPathNodeFront = curPathNodeFront + 1; + pos1 = pPathNodes[curPathNodeFront].t; + if(nextPathNodeFront < NumPathNodes) + pos2 = pPathNodes[nextPathNodeFront].t; + else{ + nextPathNodeFront = 0; + pos2 = TotalLengthOfFlightPath; + } + } + dist = pPathNodes[nextPathNodeFront].t - pPathNodes[curPathNodeFront].t; + if(dist < 0.0f) + dist += TotalLengthOfFlightPath; + f = (pathPositionFront - pPathNodes[curPathNodeFront].t)/dist; + CVector posFront = (1.0f - f)*pPathNodes[curPathNodeFront].p + f*pPathNodes[nextPathNodeFront].p; + + // And for another point 60 units in front of the plane, used to calculate roll + float pathPositionFront2 = pathPositionFront + 60.0f; + if(pathPositionFront2 > TotalLengthOfFlightPath) + pathPositionFront2 -= TotalLengthOfFlightPath; + int curPathNodeFront2 = m_nCurPathNode; + int nextPathNodeFront2 = curPathNodeFront2 + 1; + pos1 = pPathNodes[curPathNodeFront2].t; + if(nextPathNodeFront2 < NumPathNodes) + pos2 = pPathNodes[nextPathNodeFront2].t; + else{ + nextPathNodeFront2 = 0; + pos2 = TotalLengthOfFlightPath; + } + while(pathPositionFront2 < pos1 || pathPositionFront2 > pos2){ + curPathNodeFront2 = (curPathNodeFront2+1) % NumPathNodes; + nextPathNodeFront2 = curPathNodeFront2 + 1; + pos1 = pPathNodes[curPathNodeFront2].t; + if(nextPathNodeFront2 < NumPathNodes) + pos2 = pPathNodes[nextPathNodeFront2].t; + else{ + nextPathNodeFront2 = 0; + pos2 = TotalLengthOfFlightPath; + } + } + dist = pPathNodes[nextPathNodeFront2].t - pPathNodes[curPathNodeFront2].t; + if(dist < 0.0f) + dist += TotalLengthOfFlightPath; + f = (pathPositionFront2 - pPathNodes[curPathNodeFront2].t)/dist; + CVector posFront2 = (1.0f - f)*pPathNodes[curPathNodeFront2].p + f*pPathNodes[nextPathNodeFront2].p; + + // Now set matrix + GetPosition() = (posRear + posFront)/2.0f; + GetPosition().z += 4.3f; + CVector fwd = posFront - posRear; + fwd.Normalise(); + if(pitch != 0.0f){ + fwd.z += 0.4f*pitch; + fwd.Normalise(); + } + CVector fwd2 = posFront2 - posRear; + fwd2.Normalise(); + CVector roll = CrossProduct(fwd, fwd2); + CVector right = CrossProduct(fwd, CVector(0.0f, 0.0f, 1.0f)); + if(!bothOnGround) + right.z += 3.0f*roll.z; + right.Normalise(); + CVector up = CrossProduct(right, fwd); + GetRight() = right; + GetUp() = up; + GetForward() = fwd; + + // Set speed + m_vecMoveSpeed = fwd*PlanePathSpeed[m_nPlaneId]/60.0f; + m_fSpeed = PlanePathSpeed[m_nPlaneId]/60.0f; + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + + m_isFarAway = !((posFront - TheCamera.GetPosition()).Magnitude2D() < sq(300.0f)); + }else{ + float planePathPosition; + float totalLengthOfFlightPath; + CPlaneNode *pathNodes; + float planePathSpeed; + int numPathNodes; + + if(m_bIsDrugRunCesna){ + planePathPosition = PlanePath3Position; + totalLengthOfFlightPath = TotalLengthOfFlightPath3; + pathNodes = pPath3Nodes; + planePathSpeed = PlanePath3Speed; + numPathNodes = NumPath3Nodes; + if(CesnaMissionStatus == CESNA_STATUS_LANDED){ + pDrugRunCesna = false; + FlagToDestroyWhenNextProcessed(); + } + }else if(m_bIsDropOffCesna){ + planePathPosition = PlanePath4Position; + totalLengthOfFlightPath = TotalLengthOfFlightPath4; + pathNodes = pPath4Nodes; + planePathSpeed = PlanePath4Speed; + numPathNodes = NumPath4Nodes; + if(DropOffCesnaMissionStatus == CESNA_STATUS_LANDED){ + pDropOffCesna = false; + FlagToDestroyWhenNextProcessed(); + } + }else{ + planePathPosition = PlanePath2Position[m_nPlaneId]; + totalLengthOfFlightPath = TotalLengthOfFlightPath2; + pathNodes = pPath2Nodes; + planePathSpeed = PlanePath2Speed[m_nPlaneId]; + numPathNodes = NumPath2Nodes; + } + + // Advance current node to appropriate position + float pathPositionRear = planePathPosition - 10.0f; + if(pathPositionRear < 0.0f) + pathPositionRear += totalLengthOfFlightPath; + float pos1, pos2; + int nextTrackNode = m_nCurPathNode + 1; + pos1 = pathNodes[m_nCurPathNode].t; + if(nextTrackNode < numPathNodes) + pos2 = pathNodes[nextTrackNode].t; + else{ + nextTrackNode = 0; + pos2 = totalLengthOfFlightPath; + } + while(pathPositionRear < pos1 || pathPositionRear > pos2){ + m_nCurPathNode = (m_nCurPathNode+1) % numPathNodes; + nextTrackNode = m_nCurPathNode + 1; + pos1 = pathNodes[m_nCurPathNode].t; + if(nextTrackNode < numPathNodes) + pos2 = pathNodes[nextTrackNode].t; + else{ + nextTrackNode = 0; + pos2 = totalLengthOfFlightPath; + } + } + float dist = pathNodes[nextTrackNode].t - pathNodes[m_nCurPathNode].t; + if(dist < 0.0f) + dist += totalLengthOfFlightPath; + float f = (pathPositionRear - pathNodes[m_nCurPathNode].t)/dist; + CVector posRear = (1.0f - f)*pathNodes[m_nCurPathNode].p + f*pathNodes[nextTrackNode].p; + + // Same for the front + float pathPositionFront = pathPositionRear + 20.0f; + if(pathPositionFront > totalLengthOfFlightPath) + pathPositionFront -= totalLengthOfFlightPath; + int curPathNodeFront = m_nCurPathNode; + int nextPathNodeFront = curPathNodeFront + 1; + pos1 = pathNodes[curPathNodeFront].t; + if(nextPathNodeFront < numPathNodes) + pos2 = pathNodes[nextPathNodeFront].t; + else{ + nextPathNodeFront = 0; + pos2 = totalLengthOfFlightPath; + } + while(pathPositionFront < pos1 || pathPositionFront > pos2){ + curPathNodeFront = (curPathNodeFront+1) % numPathNodes; + nextPathNodeFront = curPathNodeFront + 1; + pos1 = pathNodes[curPathNodeFront].t; + if(nextPathNodeFront < numPathNodes) + pos2 = pathNodes[nextPathNodeFront].t; + else{ + nextPathNodeFront = 0; + pos2 = totalLengthOfFlightPath; + } + } + dist = pathNodes[nextPathNodeFront].t - pathNodes[curPathNodeFront].t; + if(dist < 0.0f) + dist += totalLengthOfFlightPath; + f = (pathPositionFront - pathNodes[curPathNodeFront].t)/dist; + CVector posFront = (1.0f - f)*pathNodes[curPathNodeFront].p + f*pathNodes[nextPathNodeFront].p; + + // And for another point 60 units in front of the plane, used to calculate roll + float pathPositionFront2 = pathPositionFront + 30.0f; + if(pathPositionFront2 > totalLengthOfFlightPath) + pathPositionFront2 -= totalLengthOfFlightPath; + int curPathNodeFront2 = m_nCurPathNode; + int nextPathNodeFront2 = curPathNodeFront2 + 1; + pos1 = pathNodes[curPathNodeFront2].t; + if(nextPathNodeFront2 < numPathNodes) + pos2 = pathNodes[nextPathNodeFront2].t; + else{ + nextPathNodeFront2 = 0; + pos2 = totalLengthOfFlightPath; + } + while(pathPositionFront2 < pos1 || pathPositionFront2 > pos2){ + curPathNodeFront2 = (curPathNodeFront2+1) % numPathNodes; + nextPathNodeFront2 = curPathNodeFront2 + 1; + pos1 = pathNodes[curPathNodeFront2].t; + if(nextPathNodeFront2 < numPathNodes) + pos2 = pathNodes[nextPathNodeFront2].t; + else{ + nextPathNodeFront2 = 0; + pos2 = totalLengthOfFlightPath; + } + } + dist = pathNodes[nextPathNodeFront2].t - pathNodes[curPathNodeFront2].t; + if(dist < 0.0f) + dist += totalLengthOfFlightPath; + f = (pathPositionFront2 - pathNodes[curPathNodeFront2].t)/dist; + CVector posFront2 = (1.0f - f)*pathNodes[curPathNodeFront2].p + f*pathNodes[nextPathNodeFront2].p; + + // Now set matrix + GetPosition() = (posRear + posFront)/2.0f; + GetPosition().z += 1.0f; + CVector fwd = posFront - posRear; + fwd.Normalise(); + CVector fwd2 = posFront2 - posRear; + fwd2.Normalise(); + CVector roll = CrossProduct(fwd, fwd2); + CVector right = CrossProduct(fwd, CVector(0.0f, 0.0f, 1.0f)); + right.z += 3.0f*roll.z; + right.Normalise(); + CVector up = CrossProduct(right, fwd); + GetRight() = right; + GetUp() = up; + GetForward() = fwd; + + // Set speed + m_vecMoveSpeed = fwd*planePathSpeed/60.0f; + m_fSpeed = planePathSpeed/60.0f; + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + + m_isFarAway = !((posFront - TheCamera.GetPosition()).Magnitude2D() < sq(300.0f)); + } + } + + bIsInSafePosition = true; + GetMatrix().UpdateRW(); + UpdateRwFrame(); + + // Handle streaming and such + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + if(m_isFarAway){ + // Switch to LOD model + if(m_rwObject && RwObjectGetType(m_rwObject) == rpCLUMP){ + DeleteRwObject(); + if(mi->m_planeLodId != -1){ + m_rwObject = CModelInfo::GetModelInfo(mi->m_planeLodId)->CreateInstance(); + if(m_rwObject) + m_matrix.Attach(RwFrameGetMatrix(RpAtomicGetFrame((RpAtomic*)m_rwObject))); + } + } + }else if(CStreaming::HasModelLoaded(GetModelIndex())){ + if(m_rwObject && RwObjectGetType(m_rwObject) == rpATOMIC){ + // Get rid of LOD model + m_matrix.Detach(); + if(m_rwObject){ // useless check + if(RwObjectGetType(m_rwObject) == rpATOMIC){ // useless check + RwFrame *f = RpAtomicGetFrame((RpAtomic*)m_rwObject); + RpAtomicDestroy((RpAtomic*)m_rwObject); + RwFrameDestroy(f); + } + m_rwObject = nil; + } + } + // Set high detail model + if(m_rwObject == nil){ + int id = GetModelIndex(); + m_modelIndex = -1; + SetModelIndex(id); + } + }else{ + CStreaming::RequestModel(GetModelIndex(), STREAMFLAGS_DEPENDENCY); + } +} + +void +CPlane::PreRender(void) +{ + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + CVector lookVector = GetPosition() - TheCamera.GetPosition(); + float camDist = lookVector.Magnitude(); + if(camDist != 0.0f) + lookVector *= 1.0f/camDist; + else + lookVector = CVector(1.0f, 0.0f, 0.0f); + float behindness = DotProduct(lookVector, GetForward()); + + // Wing lights + if(behindness < 0.0f){ + // in front of plane + CVector lightPos = mi->m_positions[PLANE_POS_LIGHT_RIGHT]; + CVector lightR = GetMatrix() * lightPos; + CVector lightL = lightR; + lightL -= GetRight()*2.0f*lightPos.x; + + float intensity = -0.6f*behindness + 0.4f; + float size = 1.0f - behindness; + + if(behindness < -0.9f && camDist < 50.0f){ + // directly in front + CCoronas::RegisterCorona((uintptr)this + 10, 255*intensity, 255*intensity, 255*intensity, 255, + lightL, size, 240.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_HEADLIGHTS, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + CCoronas::RegisterCorona((uintptr)this + 11, 255*intensity, 255*intensity, 255*intensity, 255, + lightR, size, 240.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_HEADLIGHTS, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + }else{ + CCoronas::RegisterCorona((uintptr)this + 10, 255*intensity, 255*intensity, 255*intensity, 255, + lightL, size, 240.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + CCoronas::RegisterCorona((uintptr)this + 11, 255*intensity, 255*intensity, 255*intensity, 255, + lightR, size, 240.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + } + } + + // Tail light + if(CTimer::GetTimeInMilliseconds() & 0x200){ + CVector pos = GetMatrix() * mi->m_positions[PLANE_POS_LIGHT_TAIL]; + + CCoronas::RegisterCorona((uintptr)this + 12, 255, 0, 0, 255, + pos, 1.0f, 120.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + } +} + +void +CPlane::Render(void) +{ + CEntity::Render(); +} + +#define CRUISE_SPEED (50.0f) +#define TAXI_SPEED (5.0f) + +void +CPlane::InitPlanes(void) +{ + int i; + + CesnaMissionStatus = CESNA_STATUS_NONE; + + // Jumbo + if(pPathNodes == nil){ + pPathNodes = LoadPath("data\\paths\\flight.dat", NumPathNodes, TotalLengthOfFlightPath, true); + + // Figure out which nodes are on ground + CColPoint colpoint; + CEntity *entity; + for(i = 0; i < NumPathNodes; i++){ + if(CWorld::ProcessVerticalLine(pPathNodes[i].p, 1000.0f, colpoint, entity, true, false, false, false, true, false, nil)){ + pPathNodes[i].p.z = colpoint.point.z; + pPathNodes[i].bOnGround = true; + }else + pPathNodes[i].bOnGround = false; + } + + // Find lading and takeoff points + LandingPoint = -1.0f; + TakeOffPoint = -1.0f; + bool lastOnGround = pPathNodes[NumPathNodes-1].bOnGround; + for(i = 0; i < NumPathNodes; i++){ + if(pPathNodes[i].bOnGround && !lastOnGround) + LandingPoint = pPathNodes[i].t; + else if(!pPathNodes[i].bOnGround && lastOnGround) + TakeOffPoint = pPathNodes[i].t; + lastOnGround = pPathNodes[i].bOnGround; + } + + // Animation + float time = 0.0f; + float position = 0.0f; + // Start on ground with slow speed + aPlaneLineBits[0].type = 1; + aPlaneLineBits[0].time = time; + aPlaneLineBits[0].position = position; + aPlaneLineBits[0].speed = TAXI_SPEED; + aPlaneLineBits[0].acceleration = 0.0f; + float dist = (TakeOffPoint-600.0f) - position; + time += dist/TAXI_SPEED; + position += dist; + + // Accelerate to take off + aPlaneLineBits[1].type = 2; + aPlaneLineBits[1].time = time; + aPlaneLineBits[1].position = position; + aPlaneLineBits[1].speed = TAXI_SPEED; + aPlaneLineBits[1].acceleration = 33.0f/32.0f; + time += 600.0f/((CRUISE_SPEED+TAXI_SPEED)/2.0f); + position += 600.0f; + + // Fly at cruise speed + aPlaneLineBits[2].type = 1; + aPlaneLineBits[2].time = time; + aPlaneLineBits[2].position = position; + aPlaneLineBits[2].speed = CRUISE_SPEED; + aPlaneLineBits[2].acceleration = 0.0f; + dist = LandingPoint - TakeOffPoint; + time += dist/CRUISE_SPEED; + position += dist; + + // Brake after landing + aPlaneLineBits[3].type = 2; + aPlaneLineBits[3].time = time; + aPlaneLineBits[3].position = position; + aPlaneLineBits[3].speed = CRUISE_SPEED; + aPlaneLineBits[3].acceleration = -33.0f/32.0f; + time += 600.0f/((CRUISE_SPEED+TAXI_SPEED)/2.0f); + position += 600.0f; + + // Taxi + aPlaneLineBits[4].type = 1; + aPlaneLineBits[4].time = time; + aPlaneLineBits[4].position = position; + aPlaneLineBits[4].speed = TAXI_SPEED; + aPlaneLineBits[4].acceleration = 0.0f; + time += (TotalLengthOfFlightPath - position)/TAXI_SPEED; + + // end + aPlaneLineBits[5].time = time; + TotalDurationOfFlightPath = time; + } + + // Dodo + if(pPath2Nodes == nil){ + pPath2Nodes = LoadPath("data\\paths\\flight2.dat", NumPath2Nodes, TotalLengthOfFlightPath2, true); + TotalDurationOfFlightPath2 = TotalLengthOfFlightPath2/CRUISE_SPEED; + } + + // Mission Cesna + if(pPath3Nodes == nil){ + pPath3Nodes = LoadPath("data\\paths\\flight3.dat", NumPath3Nodes, TotalLengthOfFlightPath3, false); + TotalDurationOfFlightPath3 = TotalLengthOfFlightPath3/CRUISE_SPEED; + } + + // Mission Cesna + if(pPath4Nodes == nil){ + pPath4Nodes = LoadPath("data\\paths\\flight4.dat", NumPath4Nodes, TotalLengthOfFlightPath4, false); + TotalDurationOfFlightPath4 = TotalLengthOfFlightPath4/CRUISE_SPEED; + } + + CStreaming::LoadAllRequestedModels(false); + CStreaming::RequestModel(MI_AIRTRAIN, 0); + CStreaming::LoadAllRequestedModels(false); + + for(i = 0; i < 3; i++){ + CPlane *plane = new CPlane(MI_AIRTRAIN, PERMANENT_VEHICLE); + plane->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); + plane->m_status = STATUS_ABANDONED; + plane->bIsLocked = true; + plane->m_nPlaneId = i; + plane->m_nCurPathNode = 0; + CWorld::Add(plane); + } + + + CStreaming::RequestModel(MI_DEADDODO, 0); + CStreaming::LoadAllRequestedModels(false); + + for(i = 0; i < 3; i++){ + CPlane *plane = new CPlane(MI_DEADDODO, PERMANENT_VEHICLE); + plane->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); + plane->m_status = STATUS_ABANDONED; + plane->bIsLocked = true; + plane->m_nPlaneId = i; + plane->m_nCurPathNode = 0; + CWorld::Add(plane); + } +} + +void +CPlane::Shutdown(void) +{ + delete[] pPathNodes; + delete[] pPath2Nodes; + delete[] pPath3Nodes; + delete[] pPath4Nodes; + pPathNodes = nil; + pPath2Nodes = nil; + pPath3Nodes = nil; + pPath4Nodes = nil; +} + +CPlaneNode* +CPlane::LoadPath(char const *filename, int32 &numNodes, float &totalLength, bool loop) +{ + int bp, lp; + int i; + + CFileMgr::LoadFile(filename, work_buff, sizeof(work_buff), "r"); + *gString = '\0'; + for(bp = 0, lp = 0; work_buff[bp] != '\n'; bp++, lp++) + gString[lp] = work_buff[bp]; + bp++; + gString[lp] = '\0'; + sscanf(gString, "%d", &numNodes); + CPlaneNode *nodes = new CPlaneNode[numNodes]; + + for(i = 0; i < numNodes; i++){ + *gString = '\0'; + for(lp = 0; work_buff[bp] != '\n'; bp++, lp++) + gString[lp] = work_buff[bp]; + bp++; + // BUG: game doesn't terminate string + gString[lp] = '\0'; + sscanf(gString, "%f %f %f", &nodes[i].p.x, &nodes[i].p.y, &nodes[i].p.z); + } + + // Calculate length of segments and path + totalLength = 0.0f; + for(i = 0; i < numNodes; i++){ + nodes[i].t = totalLength; + float l = (nodes[(i+1) % numNodes].p - nodes[i].p).Magnitude2D(); + if(!loop && i == numNodes-1) + l = 0.0f; + totalLength += l; + } + + return nodes; +} + +void +CPlane::UpdatePlanes(void) +{ + int i, j; + uint32 time; + float t, deltaT; + + if(CReplay::IsPlayingBack()) + return; + + // Jumbo jets + time = CTimer::GetTimeInMilliseconds(); + for(i = 0; i < 3; i++){ + t = TotalDurationOfFlightPath * (float)(time & 0x7FFFF)/0x80000; + // find current frame + for(j = 0; t > aPlaneLineBits[j+1].time; j++); + + OldPlanePathPosition[i] = PlanePathPosition[i]; + deltaT = t - aPlaneLineBits[j].time; + switch(aPlaneLineBits[j].type){ + case 0: // standing still + PlanePathPosition[i] = aPlaneLineBits[j].position; + PlanePathSpeed[i] = 0.0f; + break; + case 1: // moving with constant speed + PlanePathPosition[i] = aPlaneLineBits[j].position + aPlaneLineBits[j].speed*deltaT; + PlanePathSpeed[i] = (TotalDurationOfFlightPath*1000.0f/0x80000) * aPlaneLineBits[j].speed; + break; + case 2: // accelerating/braking + PlanePathPosition[i] = aPlaneLineBits[j].position + (aPlaneLineBits[j].speed + aPlaneLineBits[j].acceleration*deltaT)*deltaT; + PlanePathSpeed[i] = (TotalDurationOfFlightPath*1000.0f/0x80000)*aPlaneLineBits[j].speed + 2.0f*aPlaneLineBits[j].acceleration*deltaT; + break; + } + + // time offset for each plane + time += 0x80000/3; + } + + time = CTimer::GetTimeInMilliseconds(); + + t = TotalDurationOfFlightPath2/0x80000; + PlanePath2Position[0] = CRUISE_SPEED * (time & 0x7FFFF)*t; + PlanePath2Position[1] = CRUISE_SPEED * ((time + 0x80000/3) & 0x7FFFF)*t; + PlanePath2Position[2] = CRUISE_SPEED * ((time + 0x80000/3*2) & 0x7FFFF)*t; + PlanePath2Speed[0] = CRUISE_SPEED*t; + PlanePath2Speed[1] = CRUISE_SPEED*t; + PlanePath2Speed[2] = CRUISE_SPEED*t; + + if(CesnaMissionStatus == CESNA_STATUS_FLYING){ + PlanePath3Speed = CRUISE_SPEED*TotalDurationOfFlightPath3/0x20000; + PlanePath3Position = PlanePath3Speed * ((time - CesnaMissionStartTime) & 0x1FFFF); + if(time - CesnaMissionStartTime >= 128072) + CesnaMissionStatus = CESNA_STATUS_LANDED; + } + + if(DropOffCesnaMissionStatus == CESNA_STATUS_FLYING){ + PlanePath4Speed = CRUISE_SPEED*TotalDurationOfFlightPath4/0x80000; + PlanePath4Position = PlanePath4Speed * ((time - DropOffCesnaMissionStartTime) & 0x7FFFF); + if(time - DropOffCesnaMissionStartTime >= 521288) + DropOffCesnaMissionStatus = CESNA_STATUS_LANDED; + } +} + +bool +CPlane::TestRocketCollision(CVector *rocketPos) +{ + int i; + + i = CPools::GetVehiclePool()->GetSize(); + while(--i >= 0){ + CPlane *plane = (CPlane*)CPools::GetVehiclePool()->GetSlot(i); + if(plane && +#ifdef EXPLODING_AIRTRAIN + (plane->GetModelIndex() == MI_AIRTRAIN || plane->GetModelIndex() == MI_DODO) && +#else + plane->GetModelIndex() != MI_AIRTRAIN && plane->GetModelIndex() == MI_DODO && // strange check +#endif + !plane->m_bHasBeenHit && (*rocketPos - plane->GetPosition()).Magnitude() < 25.0f){ + plane->m_nFrameWhenHit = CTimer::GetFrameCounter(); + plane->m_bHasBeenHit = true; + CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->RegisterCrime_Immediately(CRIME_DESTROYED_CESSNA, + plane->GetPosition(), i+1983, false); + return true; + } + } + return false; +} + +// BUG: not in CPlane in the game +void +CPlane::CreateIncomingCesna(void) +{ + if(CesnaMissionStatus == CESNA_STATUS_FLYING){ + CWorld::Remove(pDrugRunCesna); + delete pDrugRunCesna; + pDrugRunCesna = nil; + } + pDrugRunCesna = new CPlane(MI_DEADDODO, PERMANENT_VEHICLE); + pDrugRunCesna->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); + pDrugRunCesna->m_status = STATUS_ABANDONED; + pDrugRunCesna->bIsLocked = true; + pDrugRunCesna->m_nPlaneId = 0; + pDrugRunCesna->m_nCurPathNode = 0; + pDrugRunCesna->m_bIsDrugRunCesna = true; + CWorld::Add(pDrugRunCesna); + + CesnaMissionStatus = CESNA_STATUS_FLYING; + CesnaMissionStartTime = CTimer::GetTimeInMilliseconds(); + printf("CPlane::CreateIncomingCesna(void)\n"); +} + +void +CPlane::CreateDropOffCesna(void) +{ + if(DropOffCesnaMissionStatus == CESNA_STATUS_FLYING){ + CWorld::Remove(pDropOffCesna); + delete pDropOffCesna; + pDropOffCesna = nil; + } + pDropOffCesna = new CPlane(MI_DEADDODO, PERMANENT_VEHICLE); + pDropOffCesna->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); + pDropOffCesna->m_status = STATUS_ABANDONED; + pDropOffCesna->bIsLocked = true; + pDropOffCesna->m_nPlaneId = 0; + pDropOffCesna->m_nCurPathNode = 0; + pDropOffCesna->m_bIsDropOffCesna = true; + CWorld::Add(pDropOffCesna); + + DropOffCesnaMissionStatus = CESNA_STATUS_FLYING; + DropOffCesnaMissionStartTime = CTimer::GetTimeInMilliseconds(); + printf("CPlane::CreateDropOffCesna(void)\n"); +} + +CVector CPlane::FindDrugPlaneCoordinates(void) { return pDrugRunCesna->GetPosition(); } +CVector CPlane::FindDropOffCesnaCoordinates(void) { return pDrugRunCesna->GetPosition(); } +bool CPlane::HasCesnaLanded(void) { return CesnaMissionStatus == CESNA_STATUS_LANDED; } +bool CPlane::HasCesnaBeenDestroyed(void) { return CesnaMissionStatus == CESNA_STATUS_DESTROYED; } +bool CPlane::HasDropOffCesnaBeenShotDown(void) { return DropOffCesnaMissionStatus == CESNA_STATUS_DESTROYED; } + + +class CPlane_ : public CPlane +{ +public: + void ctor(int32 id, uint8 CreatedBy) { ::new (this) CPlane(id, CreatedBy); } + void dtor(void) { CPlane::~CPlane(); } +}; + +STARTPATCHES + InjectHook(0x54B170, &CPlane_::ctor, PATCH_JUMP); + InjectHook(0x54B270, &CPlane_::dtor, PATCH_JUMP); + InjectHook(0x54B820, CPlane::InitPlanes, PATCH_JUMP); + InjectHook(0x54BCD0, CPlane::Shutdown, PATCH_JUMP); + InjectHook(0x54BD50, CPlane::LoadPath, PATCH_JUMP); + InjectHook(0x54BEC0, CPlane::UpdatePlanes, PATCH_JUMP); + InjectHook(0x54DE90, CPlane::TestRocketCollision, PATCH_JUMP); + InjectHook(0x54E000, CPlane::CreateIncomingCesna, PATCH_JUMP); + InjectHook(0x54E160, CPlane::CreateDropOffCesna, PATCH_JUMP); +ENDPATCHES diff --git a/src/vehicles/Vehicle.cpp b/src/vehicles/Vehicle.cpp index 775689f7..dd15d910 100644 --- a/src/vehicles/Vehicle.cpp +++ b/src/vehicles/Vehicle.cpp @@ -273,7 +273,7 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon right = -contactSpeedRight/wheelsOnGround; if(wheelStatus == WHEEL_STATUS_BURST){ - float fwdspeed = min(contactSpeedFwd, 0.3f); + float fwdspeed = Min(contactSpeedFwd, 0.3f); right += fwdspeed * CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); } } @@ -363,7 +363,7 @@ CVehicle::ProcessWheelRotation(tWheelState state, const CVector &fwd, const CVec void CVehicle::ExtinguishCarFire(void) { - m_fHealth = max(m_fHealth, 300.0f); + m_fHealth = Max(m_fHealth, 300.0f); if(m_pCarFire) m_pCarFire->Extinguish(); if(IsCar()){ @@ -638,13 +638,13 @@ CVehicle::SetDriver(CPed *driver) if(bFreebies && driver == FindPlayerPed()){ if(GetModelIndex() == MI_AMBULAN) - FindPlayerPed()->m_fHealth = min(FindPlayerPed()->m_fHealth + 20.0f, 100.0f); + FindPlayerPed()->m_fHealth = Min(FindPlayerPed()->m_fHealth + 20.0f, 100.0f); else if(GetModelIndex() == MI_TAXI) CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 25; else if(GetModelIndex() == MI_POLICE) driver->GiveWeapon(WEAPONTYPE_SHOTGUN, 5); else if(GetModelIndex() == MI_ENFORCER) - driver->m_fArmour = max(driver->m_fArmour, 100.0f); + driver->m_fArmour = Max(driver->m_fArmour, 100.0f); else if(GetModelIndex() == MI_CABBIE || GetModelIndex() == MI_BORGNINE) CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 25; bFreebies = false; |